bzoj3626[LNOI2014]LCA
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3626
竟然是这样看。
深度可以差分表示。一个点的深度计入贡献可以表示成给它到根的链上所有点的值+1。LCA的深度就变成对方到根节点的链上的值。
l~r与z的LCA的深度和 就是把 l~r 都这样做一遍,然后求z到根的链上的值。
然后就变成树链剖分模板。
这里要对每个 l 和 r 排序,枚举计入贡献的点从1到n,达到一个前缀的效果(1~r - 1~(l-1))。
因为z各有不同,所以最好一遇到某个询问的 l 或 r 就算一下。不然岂不是要记录所有 z 对应的前缀和?
注意的一点是init里的那个地方。要先调一调。
(不知为何自己的代码比别人慢!仔细一想不会爆long long所以把中间的取模去掉,还是那么慢!)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=5e4+5;const ll mod=201314; int n,m,head[N],tot,dl,dr; int fa[N],son[N],siz[N],rnk[N],top[N]; struct Node{ int ls,rs;ll val,laz,cd; }a[N<<1]; struct Ques{ int l,r,z; ll ansl,ansr; }q[N]; struct Edge{ int next,to; }edge[N]; struct Tmp{ int v,id; }tl[N],tr[N]; bool cmp(Tmp a,Tmp b){return a.v<b.v;} int rdn() { int ret=0;char ch=getchar(); while(ch>'9'||ch<'0')ch=getchar(); while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar(); return ret; } void dfs1(int cr) { siz[cr]=1; for(int i=head[cr],v;i;i=edge[i].next) if((v=edge[i].to)!=fa[cr]) { dfs1(v);siz[cr]+=siz[v]; if(siz[v]>siz[son[cr]])son[cr]=v; } } void dfs2(int cr) { rnk[cr]=++tot; if(!son[cr])return; top[son[cr]]=top[cr];dfs2(son[cr]); for(int i=head[cr],v;i;i=edge[i].next) if((v=edge[i].to)!=fa[cr]&&v!=son[cr]) { top[v]=v;dfs2(v); } } void build(int l,int r,int cr) { if(l==r){a[cr].cd=1;return;} a[cr].cd=r-l+1; int mid=((l+r)>>1); a[cr].ls=++tot;build(l,mid,tot); a[cr].rs=++tot;build(mid+1,r,tot); } void init() { dfs1(1);top[1]=1;dfs2(1);tot=1;build(1,n,1); for(dl=1;dl<=m&&tl[dl].v<=1;dl++); for(dr=1;dr<=m&&!tr[dr].v;dr++);// } void pushdown(int cr) { int ls=a[cr].ls,rs=a[cr].rs;ll laz=a[cr].laz;a[cr].laz=0; a[ls].laz+=laz;a[rs].laz+=laz; a[ls].val+=a[ls].cd*laz;a[rs].val+=a[rs].cd*laz; } void pushup(int cr) { a[cr].val=a[a[cr].ls].val+a[a[cr].rs].val; } void pls(int l,int r,int cr,int L,int R) { if(l>=L&&r<=R) { a[cr].val+=a[cr].cd;a[cr].laz++;return; } pushdown(cr); int mid=((l+r)>>1); if(mid>=L)pls(l,mid,a[cr].ls,L,R); if(mid<R)pls(mid+1,r,a[cr].rs,L,R); pushup(cr); } ll qry(int l,int r,int cr,int L,int R) { if(l>=L&&r<=R)return a[cr].val; pushdown(cr); int mid=((l+r)>>1);ll ret=0; if(mid>=L)ret+=qry(l,mid,a[cr].ls,L,R); if(mid<R)ret+=qry(mid+1,r,a[cr].rs,L,R); pushup(cr);return ret; } void add(int k) { while(k) { pls(1,n,1,rnk[top[k]],rnk[k]);k=fa[top[k]]; } } ll query(int k) { ll ret=0; while(k) { ret+=qry(1,n,1,rnk[top[k]],rnk[k]);k=fa[top[k]]; } return ret; } int main() { n=rdn();m=rdn(); for(int i=2;i<=n;i++) { fa[i]=rdn()+1;edge[i].next=head[fa[i]];edge[i].to=i;head[fa[i]]=i; } for(int i=1;i<=m;i++) { tl[i].v=q[i].l=rdn()+1;tr[i].v=q[i].r=rdn()+1;q[i].z=rdn()+1; tl[i].id=i;tr[i].id=i; } sort(tl+1,tl+m+1,cmp);sort(tr+1,tr+m+1,cmp); init(); for(int i=1;i<=n;i++) { add(i); while(dl<=m&&tl[dl].v-1==i)q[tl[dl].id].ansl=query(q[tl[dl++].id].z); while(dr<=m&&tr[dr].v==i)q[tr[dr].id].ansr=query(q[tr[dr++].id].z); } for(int i=1;i<=m;i++)printf("%lld\n",(q[i].ansr-q[i].ansl)%mod);// return 0; }