Loj #6073.「2017 山东一轮集训 Day5」距离
Description
给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\),设$ path(u,v) \(表示\) u$ 到 $v \(的路径,\)dist(u,v) \(表示\) u$ 到\(v\) 的距离,希望你求出
Input
第一行一个整数 \(type =0/1\)表示这个测试点的数据类型。
第二行两个整数 \(n,q\)。 接下来$ n−1$ 行,每行三个整数 \(ui,vi,ci,\)代表树上有一条连接$ ui,vi$ 的权值为$ci $的边。 接下来一行 \(n\) 个正整数表示给定的排列 p。 接下来 \(q\) 行,每行三个整数 \(u′,v′,k′\),记lastAns 为上一次询问的答案,假如这是第一次则\(lastAns=0\),那么这个询问对应的\(u,v,k\) 满足:思路还是比较妙啊。
题目的难点在于求一个点与一个点集的\(lca\)深度之和。我们可以将点集中的每个点到根的路径上的标记都\(+1\)。询问点\(k\)到这个点集的\(lca\)深度和的时候我们就可以询问该点到根路径上的所有边权与标记的乘积之和。
由于是询问\((a,b)\)路径上的信息,我们就用主席树维护,询问的时候做差分。对于一个节点\(a\),它的信息由\(fa_a\)继承下来,再加上\(p_a\)的信息就好了。
考试时不会,写的虚树,常数大到自闭。
[HNOI2015]开店 的主席树做法也是基于这个原理的。
代码:
#include#define ll long long#define N 200005using namespace std;inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}int n,m,type;int p[N];ll pre[N];ll ans;struct road { int to,next; ll d;}s[N<<1];int h[N],cnt;void add(int i,int j,ll d) {s[++cnt]=(road) {j,h[i],d};h[i]=cnt;}ll sum_dis[N];int size[N],son[N];int fa[N],top[N],dep[N];ll dis[N];void dfs(int v) { size[v]=1; for(int i=h[v];i;i=s[i].next) { int to=s[i].to; if(to==fa[v]) continue ; dep[to]=dep[v]+1; dis[to]=dis[v]+s[i].d; fa[to]=v; dfs(to); size[v]+=size[to]; if(size[son[v]] r||rx >1; Modify(tr[v].ls,tr[old].ls,lx,mid,l,r,f); Modify(tr[v].rs,tr[old].rs,mid+1,rx,l,r,f);}void Modify(int v) { int a=p[v]; while(top[a]!=top[1]) { Modify(rt[v],rt[v],lx,rx,dfn[top[a]],dfn[a],1); a=fa[top[a]]; } Modify(rt[v],rt[v],lx,rx,dfn[1],dfn[a],1);}ll query(int v,int lx,int rx,int l,int r) { if(!v||lx>r||rx >1; ans+=query(tr[v].ls,lx,mid,l,r)+query(tr[v].rs,mid+1,rx,l,r); return ans;}ll query(int rt,int a) { ll ans=0; while(top[a]!=top[1]) { ans+=query(rt,lx,rx,dfn[top[a]],dfn[a]); a=fa[top[a]]; } ans+=query(rt,lx,rx,dfn[1],dfn[a]); return ans;}void dfs3(int v) { rt[v]=rt[fa[v]]; Modify(v); for(int i=h[v];i;i=s[i].next) { int to=s[i].to; if(to==fa[v]) continue ; dfs3(to); }}int main() { type=Get(); n=Get(),m=Get(); int a,b,d; for(int i=1;i