题解 P4211 【[LNOI2014]LCA】

Great_Influence

2018-02-06 16:33:30

Solution

感谢hzwer大佬的博客指示...... 可以发现,问题能够转化为从询问点到根都加1,然后询问l到r的点到根的路径上权值之和。然后这个问题又和“l到r的点到根路径上所有点权值分别+1,求询问点到根路径上的权值之和”等价。 然后就可做了。将询问差分一下,变成查询$[1,r]-[1,l-1]$。这时候只需要离线询问,在l-1和r处分别打标记,在从1~n依次遍历,扫到标记就查询,用树链剖分套线段树维护。最后将答案全部输出即可。时间复杂度$O(n\log_2^2n)$。 代码:(笔记本打的,缩进有点丑) ```cpp #include<bits/stdc++.h> #define For(i,a,b) for(i=(a);i<=(b);++i) #define Forward(i,a,b) for(i=(a);i>=(b);--i) #define Rep(i,a,b) for(register int i=(a),i##end=(b);i<=i##end;++i) #define Repe(i,a,b) for(register int i=(a),i##end=(b);i>=i##end;--i) using namespace std; template<typename T>inline void read(T &x) { T s=0,f=1;char k=getchar(); while(!isdigit(k)&&(k^'-'))k=getchar(); if(!isdigit(k)){f=-1;k=getchar();} while(isdigit(k)){s=s*10+(k^48);k=getchar();} x=s*f; } void file() { #ifndef ONLINE_JUDGE freopen("test.in","r",stdin); freopen("test.out","w",stdout); #endif } #define Chkmax(a,b) a=a>(b)?a:(b) #define Chkmin(a,b) a=a<(b)?a:(b) const int MAXN=5e4+7; static struct edge { int v,nxt; }p[MAXN]; static int n,m,e,head[MAXN],dep[MAXN]; static int fa[MAXN],sz[MAXN],son[MAXN]; inline void add(int u,int v) {p[++e].v=v;p[e].nxt=head[u];head[u]=e;} void dfs(int u) { sz[u]=1;dep[u]=dep[fa[u]]+1; for(register int v=head[u];v;v=p[v].nxt) { dfs(p[v].v);sz[u]+=sz[p[v].v]; if(!son[u]||sz[p[v].v]>sz[son[u]]) son[u]=p[v].v; } } static int top[MAXN],dfn[MAXN],ri[MAXN]; void dfs(int u,int tp) { ri[dfn[u]=++e]=u;top[u]=tp; if(son[u])dfs(son[u],tp); else return; for(register int v=head[u];v;v=p[v].nxt) if(p[v].v^son[u])dfs(p[v].v,p[v].v); } const int mod=201314; static vector<int>pl[MAXN],mi[MAXN]; static int ask[MAXN]; inline void init() { read(n);read(m); Rep(i,2,n)read(fa[i]),add(++fa[i],i); dfs(1);e=0;dfs(1,1); static int l,r; Rep(i,1,m)read(l),read(r),read(ask[i]) ,mi[l].push_back(i),pl[r+1].push_back(i) ,++ask[i]; } static int sum[MAXN<<2],laz[MAXN<<2]; static int ans[MAXN]; inline void pushdown(int h,int l,int r) { if(laz[h]) { static int mid;mid=(l+r)>>1; sum[h<<1]=(sum[h<<1] +1ll*laz[h]*(mid-l+1)%mod)%mod; sum[h<<1|1]=(sum[h<<1|1] +1ll*laz[h]*(r-mid)%mod)%mod; laz[h<<1]+=laz[h];laz[h<<1|1]+=laz[h]; laz[h]=0; } } void modify(int h,int l,int r,int x,int y) { if(l>=x&&r<=y) { sum[h]=(sum[h]+r-l+1)%mod; ++laz[h];return; } pushdown(h,l,r); int mid=(l+r)>>1; if(x<=mid)modify(h<<1,l,mid,x,y); if(y>mid)modify(h<<1|1,mid+1,r,x,y); sum[h]=(sum[h<<1]+sum[h<<1|1])%mod; } inline void modi(int x) { while(x) { modify(1,1,n,dfn[top[x]],dfn[x]) ,x=fa[top[x]];} } static vector<int>::iterator it; inline int query(int h,int l,int r,int x,int y) { if(l>=x&&r<=y)return sum[h]; pushdown(h,l,r); int mid=(l+r)>>1,ans=0; if(x<=mid)ans=query(h<<1,l,mid,x,y); if(y>mid)ans=(ans+query(h<<1|1,mid+1,r,x,y))%mod; return ans; } inline int qu(int x) { static int ans;ans=0; while(x) { ans=(ans+query(1,1,n,dfn[top[x]],dfn[x])); x=fa[top[x]]; } return ans; } inline void solve() { Rep(i,1,n) { modi(i); for(it=pl[i].begin();it!=pl[i].end();++it) ans[*it]+=qu(ask[*it]); for(it=mi[i].begin();it!=mi[i].end();++it) ans[*it]-=qu(ask[*it]); } Rep(i,1,m)printf("%d\n",(ans[i]%mod+mod)%mod); } int main() { file(); init(); solve(); return 0; } ``` ~~其实还能用LCT优化到$O(n\log_2n)$,但是太麻烦了......$~~