Luogu P4211 [LNOI2014]LCA
阿新 • • 發佈:2018-07-28
強制 edge std putc ring swa return ron mem
我去這道題的Luogu評級是假的吧,這都算黑題。
我們首先考慮把操作離線不強制在線的題目離線一下一般都要方便些
考慮差分,我們用\(f(x)\)表示\([1,x]\)之間的點與\(z\)的答案,那麽顯然\(f(r)-f(l-1)\)即為每一次的答案。
考慮煩人的LCA,我們難以直接處理除非你會快速地一次求出一堆點的LCA
然後我們考慮從LCA的性質入手,考慮我們最初始的方法求LCA:暴力向上跳。
我們在最初始時對於一個點在它向上到根的路徑上都打上標記,然後對於另一個店也沿著它向上到根的路徑尋找,第一個被標記的點就是它們的LCA。正確性顯然
然後我們神奇的發現:這種看上去最SB的方法可以神奇的解決這道題。
我們對於每一個點,都將它到根的路徑上的點權值加一
然後對於每次詢問的\(z\),直接查詢到根的鏈上所有點的權值和即可。
維護的方式也很顯然了,對於這種樹上路徑修改/查詢的問題,樹鏈剖分是再好不過了。
CODE
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const int N=50005,mod=201314; struct edge { int to,next; }e[N]; struct data { int id,pos,z; bool exist; }q[N<<1]; struct segtree { int sum,add; }tree[N<<2]; int head[N],cnt,n,m,x,y,z,rt=1,ans1[N],ans2[N],father[N],id[N],tot,son[N],dep[N],top[N],size[N],now; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-‘0‘,isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+‘0‘); } inline void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } inline bool cmp(data a,data b) { return a.pos<b.pos; } inline void swap(int &a,int &b) { int t=a; a=b; b=t; } inline void inc(int &x,int y) { if ((x+=y)>=mod) x-=mod; } inline void pushup(int rt) { tree[rt].sum=(tree[rt<<1].sum+tree[rt<<1|1].sum)%mod; } inline void down(int rt,int l,int r) { if (tree[rt].add) { inc(tree[rt<<1].add,tree[rt].add); inc(tree[rt<<1|1].add,tree[rt].add); inc(tree[rt<<1].sum,(tree[rt].add*l)%mod); inc(tree[rt<<1|1].sum,(tree[rt].add*r)%mod); tree[rt].add=0; } } inline void modify(int rt,int l,int r,int beg,int end) { if (l>=beg&&r<=end) { inc(tree[rt].sum,r-l+1); inc(tree[rt].add,1); return; } int mid=l+r>>1; down(rt,mid-l+1,r-mid); if (beg<=mid) modify(rt<<1,l,mid,beg,end); if (end>mid) modify(rt<<1|1,mid+1,r,beg,end); pushup(rt); } inline int query(int rt,int l,int r,int beg,int end) { if (l>=beg&&r<=end) return tree[rt].sum; int mid=l+r>>1,res=0; down(rt,mid-l+1,r-mid); if (beg<=mid) inc(res,query(rt<<1,l,mid,beg,end)); if (end>mid) inc(res,query(rt<<1|1,mid+1,r,beg,end)); pushup(rt); return res; } inline void DFS1(int now,int fa,int d) { dep[now]=d; father[now]=fa; size[now]=1; int res=-1; for (register int i=head[now];~i;i=e[i].next) { DFS1(e[i].to,now,d+1); size[now]+=size[e[i].to]; if (size[e[i].to]>res) res=size[e[i].to],son[now]=e[i].to; } } inline void DFS2(int now,int topf) { top[now]=topf; id[now]=++tot; if (!son[now]) return; DFS2(son[now],topf); for (register int i=head[now];~i;i=e[i].next) if (e[i].to!=son[now]) DFS2(e[i].to,e[i].to); } inline void updata(int x,int y) { if (dep[x]<dep[y]) swap(x,y); while (top[x]!=top[y]) { modify(1,1,n,id[top[x]],id[x]); x=father[top[x]]; } if (dep[x]<dep[y]) swap(x,y); modify(1,1,n,id[y],id[x]); } inline int get_link(int x,int y) { int res=0; if (dep[x]<dep[y]) swap(x,y); while (top[x]!=top[y]) { inc(res,query(1,1,n,id[top[x]],id[x])); x=father[top[x]]; } if (dep[x]<dep[y]) swap(x,y); inc(res,query(1,1,n,id[y],id[x])); return res; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(n); read(m); memset(head,-1,sizeof(head)); for (i=2;i<=n;++i) read(x),add(x+1,i); for (i=1;i<=m;++i) { read(x); read(y); read(z); q[(i<<1)-1]=(data){i,x,z+1,0}; q[i<<1]=(data){i,y+1,z+1,1}; } sort(q+1,q+(m<<1)+1,cmp); DFS1(rt,-1,0); DFS2(rt,rt); for (i=1;i<=(m<<1);++i) { while (now<q[i].pos) updata(rt,++now); if (q[i].exist) ans2[q[i].id]=get_link(rt,q[i].z); else ans1[q[i].id]=get_link(rt,q[i].z); } for (i=1;i<=m;++i) write((ans2[i]-ans1[i]+mod)%mod),putchar(‘\n‘); return 0; }
Luogu P4211 [LNOI2014]LCA