bzoj3891
学长的题解:显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] ? [1, l ? 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n ? 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。
1.树链剖分
#include<cstdio> #include<cctype> #include<algorithm> #define maxn 50001 #define mo 201314 using namespace std; int n,m,cnt,head[maxn],to[maxn],nex[maxn]; int dep[maxn],son[maxn],a[maxn],id[maxn],top[maxn],siz[maxn],fa[maxn]; struct data{int p,id,z;bool vis;}q[maxn<<1]; struct result{int ans1,ans2;}ans[maxn]; struct tree{int l,r,sum,tag;}tr[maxn<<2]; inline bool operator<(const data a,const data b){return a.p<b.p;} inline void read(int &x){ char ch=getchar();x=0; while(!isdigit(ch))ch=getchar(); while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} } void addedge(int u,int v){ to[++cnt]=v;nex[cnt]=head[u];head[u]=cnt; } void dfs1(int x,int f){ dep[x]=dep[f]+1;fa[x]=f;siz[x]=1; int maxson=-1; for(int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==f)continue; dfs1(y,x); siz[x]+=siz[y]; if(siz[y]>maxson){maxson=siz[y];son[x]=y;} } } void dfs2(int x,int topf){ id[x]=++cnt; a[cnt]=x;top[x]=topf; if(!son[x])return; dfs2(son[x],topf); for(int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==fa[x]||y==son[x])continue; dfs2(y,y); } } void buildtr(int now,int l,int r){ tr[now].l=l;tr[now].r=r; if(l==r)return; int mid=(l+r)>>1; buildtr(now<<1,l,mid);buildtr(now<<1|1,mid+1,r); } void pushdown(int now){ if(tr[now].l==tr[now].r||!tr[now].tag)return; tr[now<<1].sum=(tr[now<<1].sum+(tr[now<<1].r-tr[now<<1].l+1)*tr[now].tag)%mo;tr[now<<1].tag=(tr[now<<1].tag+tr[now].tag)%mo; tr[now<<1|1].sum=(tr[now<<1|1].sum+(tr[now<<1|1].r-tr[now<<1|1].l+1)*tr[now].tag)%mo;tr[now<<1|1].tag=(tr[now<<1|1].tag+tr[now].tag)%mo; tr[now].tag=0; } void updata(int now,int l,int r){ pushdown(now); if(tr[now].l==l&&tr[now].r==r){ tr[now].tag++;tr[now].sum+=tr[now].r-tr[now].l+1; return; } int mid=(tr[now].l+tr[now].r)>>1; if(mid>=r)updata(now<<1,l,r);else if(mid<l)updata(now<<1|1,l,r);else{ updata(now<<1,l,mid);updata(now<<1|1,mid+1,r); } tr[now].sum=(tr[now<<1].sum+tr[now<<1|1].sum)%mo;//注意这里(l,r)变成(l,mid)(mid+1,r)因此RE了好几次 } void solve_updata(int x,int y){ while(top[x]!=top[y]){ updata(1,id[top[x]],id[x]); x=fa[top[x]]; } updata(1,id[y],id[x]); } int query(int now,int l,int r){ pushdown(now); if(tr[now].l==l&&tr[now].r==r)return tr[now].sum; int mid=(tr[now].l+tr[now].r)>>1; if(mid>=r)return query(now<<1,l,r); if(mid<l)return query(now<<1|1,l,r); return query(now<<1,l,mid)+query(now<<1|1,mid+1,r);//注意这里(l,r)变成(l,mid+1)和(mid+1,r)因此RE了好几次 } int solve_query(int x,int y){ int res=0; while(top[x]!=top[y]){ res=(res+query(1,id[top[x]],id[x]))%mo; x=fa[top[x]]; } res=(res+query(1,id[y],id[x]))%mo; return res; } int main(){ read(n);read(m); for(int i=1;i<n;i++){ int x;read(x);addedge(x,i); } cnt=0; dfs1(0,0);dfs2(0,0); cnt=0; for(int i=1;i<=m;i++){ int l,r,z;read(l);read(r);read(z); q[++cnt].p=l-1;q[cnt].id=i;q[cnt].z=z;q[++cnt].p=r;q[cnt].id=i;q[cnt].z=z;q[cnt].vis=1; } sort(q+1,q+cnt+1); buildtr(1,1,n); int now=-1; for(int i=1;i<=cnt;i++){ while(now<q[i].p)solve_updata(++now,0); int id=q[i].id; if(!q[i].vis)ans[id].ans1=solve_query(q[i].z,0);else ans[id].ans2=solve_query(q[i].z,0); } for(int i=1;i<=m;i++)printf("%d\n",(ans[i].ans2-ans[i].ans1+mo)%mo); }