1. 程式人生 > >bzoj2759: 一個動態樹好題

bzoj2759: 一個動態樹好題

i++ can n) 合並 color names 一個點 get 如何

LCT補坑。。。

經過我多年被樹形思路題的狂虐加上LCT路牌的提示,終於想到了對於每個未知數建一個點建樹

觀察柿子,它只有二元,我們可以理解為i被pi表示,那麽pi在樹上作為i的父親,理解為i向pi連邊,那麽這個圖就是一個內向基環樹森林

對於每棵基環樹把環斷開,以斷開的出點為根,記錄根被誰表示

如何解方程呢?我們可以這麽做:把樹上的每一個點都用表示出根的未知數表示,用一個未知數表示另一個未知數,用的就是k和b

既然如此我們定義二元組(k,b)表示kx+b=當前未知數,x我們定

那麽直接輸進來的k和b表示的就是用父親表示當前節點

要用表示出根的點表示當前節點,相當於要把當前點到根的k,b二元組合並

稍微推一下:x0 = k1x1 + b1 = k1( k2x2 + b2 ) + b1 = k1k2x2 + k1b2 + b1

這樣的和並定義為(k2,b2)+(k1,b1),則有(k2,b2)+(k1,b1)=( k1k2 , k1b2+b1 )

於是要維護的就是根到當前點二元組之和,註意不能反過來,不滿足交換律

可以splay維護,記錄一下子樹中的點的二元組按中序遍歷加起來的和即可,那麽對於當前點的影響就是左孩子的和+自己的二元組

值得註意的是,cut的時候可能導致環的斷裂,這時可以把根的出邊再次嘗試連接來避免出鍋

#include<cstdio>
#include
<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; const int mod=10007; int exgcd(int a,int b,int &x,int &y) { if(a==0) { x=0;y=1; return b; } else { int
tx,ty; int d=exgcd(b%a,a,tx,ty); x=ty-b/a*tx; y=tx; return d; } } int solve(int A,int B,int K) { int x,y,d=exgcd(A,B,x,y); if(K%d!=0)return -1; else { x=(x*(K/d)%(B/d)+(B/d))%(B/d); y=(A*x-K)/B; return y; } } //--------------------------------------tool------------------------------------------------------- struct pa { int k,b; pa(){k=1,b=0;} pa(int K,int B){k=K,b=B;} friend pa operator +(pa p1,pa p2){return pa((p1.k*p2.k)%mod,(p2.k*p1.b+p2.b)%mod);} }; struct trnode { int f,son[2];//base pa u,p;//p維護的是子樹中中序遍歷u和 //左孩子的中序遍歷u和+自己的u=我用實根表示的方案 }tr[31000]; void update(int x) { int lc=tr[x].son[0],rc=tr[x].son[1]; tr[x].p=pa(1,0); if(lc!=0)tr[x].p=(tr[x].p+tr[lc].p); tr[x].p=(tr[x].p+tr[x].u); if(rc!=0)tr[x].p=(tr[x].p+tr[rc].p); } void rotate(int x,int w) { int f=tr[x].f,ff=tr[f].f; int R,r; R=f,r=tr[x].son[w]; tr[R].son[1-w]=r; if(r!=0)tr[r].f=R; R=ff,r=x; if(R>0) { if(tr[ff].son[0]==f)tr[R].son[0]=r; else if(tr[ff].son[1]==f)tr[R].son[1]=r; } tr[r].f=R; R=x,r=f; tr[R].son[w]=r; tr[r].f=R; update(f); update(x); } bool isroot(int x) { int f=tr[x].f; if(f<0||(tr[f].son[0]!=x&&tr[f].son[1]!=x))return true; return false; } void splay(int x) { while(isroot(x)==false) { int f=tr[x].f,ff=tr[f].f; if(isroot(f)==true) { if(tr[f].son[0]==x)rotate(x,1); else if(tr[f].son[1]==x)rotate(x,0); } else { if(tr[ff].son[0]==f&&tr[f].son[0]==x)rotate(f,1),rotate(x,1); else if(tr[ff].son[1]==f&&tr[f].son[1]==x)rotate(f,0),rotate(x,0); else if(tr[ff].son[0]==f&&tr[f].son[1]==x)rotate(x,0),rotate(x,1); else if(tr[ff].son[1]==f&&tr[f].son[0]==x)rotate(x,1),rotate(x,0); } } } //~~~~~~~~~~~~~~~~~~~splay~~~~~~~~~~~~~~~~~~~~~~~~~ void access(int x) { int y=0; while(x>0) { splay(x); tr[x].son[1]=y; update(x); y=x;x=tr[x].f; } } int findroot(int x) { access(x);splay(x); while(tr[x].son[0]!=0)x=tr[x].son[0]; return x; } //~~~~~~~~~~~~~~~~~~~~~~in~~~~~~~~~~~~~~~~~~~~~~~~~ void Link(int x,int y) { if(findroot(x)==findroot(y)) { access(x),splay(x); tr[x].f=-y; } else { access(x);splay(x); tr[x].f=y; } } void Cut(int x) { access(x);splay(x); if(tr[x].son[0]==0)tr[x].f=0; else { int y=tr[x].son[0],p=-tr[x].f; tr[y].f=0,tr[x].son[0]=0; Link(findroot(y),p); update(x); } } pa getpa(int x) { access(x),splay(x); return tr[tr[x].son[0]].p+tr[x].u; } int ask(int x) { pa g=getpa(x),e=getpa(-tr[x].f); e.k--;if(e.k<0)e.k+=mod; if(e.k==0)return -1-(e.b==e.b); int d=solve(mod,e.k,e.b); if(d==-1)return -1; else return (g.k*d+g.b)%mod; } //~~~~~~~~~~~~~~~~~~~~~out~~~~~~~~~~~~~~~~~~~~~~~~~ //-------------------------------------------------lCT----------------------------------------------------------------------- char ss[5]; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n,F; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d%d",&tr[i].u.k,&F,&tr[i].u.b); tr[i].p=tr[i].u; Link(i,F); } int Q,x; scanf("%d",&Q); while(Q--) { scanf("%s%d",ss+1,&x); if(ss[1]==A)printf("%d\n",ask(x)); else { scanf("%d%d%d",&tr[x].u.k,&F,&tr[x].u.b); Cut(x);Link(x,F); } } return 0; }

bzoj2759: 一個動態樹好題