【传送门:BZOJ3626】
简要题意:
给出一棵树,设dep[i]为每个点与根节点的距离+1,
有m个询问,每个询问输入l,r,z
求出$\sum_{l<=i<=r}dep[LCA(i,z)]$
题解:
读题,实际上dep[i]就表示i到根有多少个节点
那么如果要求dep[LCA(i,j)]的话,就将i到根节点上的所有点权都+1,然后求j到根节点的路径的点权和就可以了(自行yy)
然后肯定不能在线做,因为太大了
那就离线吧
开开心心打莫队,T了。。。O(mn0.5)都超时
好吧,想另一种离线,看看,怎么和前缀和什么的有点关系
直接将询问分成两个,一个是询问1到l-1,另一个是询问1到r
然后分别记录在这两段区间里z向根节点的路径的点权和,然后把第二个区间得到的值-第一个区间得到的值就行了
很显然是成立的
然后就没了。
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; struct node { int x,y,next; }a[51000];int len,last[51000]; int Mod=201314; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } struct trnode { int l,r,lc,rc; LL c,lazy; }tr[110000];int trlen; void bt(int l,int r) { trlen++;int now=trlen; tr[now].l=l;tr[now].r=r;tr[now].c=tr[now].lazy=0; tr[now].lc=tr[now].rc=-1; if(l<r) { int mid=(l+r)/2; tr[now].lc=trlen+1;bt(l,mid); tr[now].rc=trlen+1;bt(mid+1,r); } } int tot[51000],dep[51000],fa[51000],son[51000]; void pre_tree_node(int x) { tot[x]=1;son[x]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]) { dep[y]=dep[x]+1; fa[y]=x; pre_tree_node(y); tot[x]+=tot[y]; if(tot[y]>tot[son[x]]) son[x]=y; } } } int ys[51000],z,top[51000]; void pre_tree_edge(int x,int tp) { ys[x]=++z;top[x]=tp; if(son[x]!=0) pre_tree_edge(son[x],tp); for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]&&y!=son[x]) pre_tree_edge(y,y); } } void update(int now) { int lc=tr[now].lc,rc=tr[now].rc; if(lc!=-1) { tr[lc].c=(tr[lc].c+(tr[lc].r-tr[lc].l+1)*tr[now].lazy)%Mod; tr[lc].lazy=(tr[lc].lazy+tr[now].lazy)%Mod; } if(rc!=-1) { tr[rc].c=(tr[rc].c+(tr[rc].r-tr[rc].l+1)*tr[now].lazy)%Mod; tr[rc].lazy=(tr[rc].lazy+tr[now].lazy)%Mod; } tr[now].lazy=0; } void change(int now,int l,int r,int c) { if(tr[now].l==l&&tr[now].r==r) { tr[now].c=(tr[now].c+c*LL(tr[now].r-tr[now].l+1))%Mod; tr[now].lazy=(tr[now].lazy+c)%Mod; return ; } int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2; if(tr[now].lazy!=0) update(now); if(r<=mid) change(lc,l,r,c); else if(l>mid) change(rc,l,r,c); else change(lc,l,mid,c),change(rc,mid+1,r,c); tr[now].c=(tr[lc].c+tr[rc].c)%Mod; } void modify(int x,int c) { int tx=top[x]; while(x!=0) { change(1,ys[tx],ys[x],c); x=fa[tx];tx=top[x]; } } LL getsum(int now,int l,int r) { if(tr[now].l==l&&tr[now].r==r) return tr[now].c; int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2; if(tr[now].lazy!=0) update(now); if(r<=mid) return getsum(lc,l,r)%Mod; else if(l>mid) return getsum(rc,l,r)%Mod; else return (getsum(lc,l,mid)+getsum(rc,mid+1,r))%Mod; } LL solve(int x) { int tx=top[x];LL ans=0; while(x!=0) { ans=(ans+getsum(1,ys[tx],ys[x]))%Mod; x=fa[tx];tx=top[x]; } return ans; } struct cc { int l,r,z,id;LL d; }c[51000],q[110000]; bool cmp1(cc n1,cc n2) { return n1.r<n2.r; } bool cmp2(cc n1,cc n2) { return n1.id<n2.id; } int main() { int n,m; scanf("%d%d",&n,&m); len=0;memset(last,0,sizeof(last)); for(int i=2;i<=n;i++) { int f; scanf("%d",&f);f++; ins(f,i); } dep[1]=0;fa[1]=0;pre_tree_node(1); z=0;pre_tree_edge(1,1); trlen=0;bt(1,z); for(int i=1;i<=m;i++) { scanf("%d%d%d",&c[i].l,&c[i].r,&c[i].z);c[i].l++;c[i].r++;c[i].z++; q[2*i-1].r=c[i].l-1;q[2*i-1].z=c[i].z;q[2*i-1].id=2*i-1; q[2*i].r=c[i].r;q[2*i].z=c[i].z;q[2*i].id=2*i; } sort(q+1,q+2*m+1,cmp1); int r=0; for(int i=1;i<=2*m;i++) { while(r<q[i].r) { r++; modify(r,1); } q[i].d=solve(q[i].z); } sort(q+1,q+2*m+1,cmp2); for(int i=1;i<=m;i++) printf("%lld\n",(q[2*i].d-q[2*i-1].d+Mod)%Mod); return 0; }