bzoj2759: 一个动态树好题
LCT补坑。。。
经过我多年被树形思路题的狂虐加上LCT路牌的提示,终于想到了对于每个未知数建一个点建树
观察柿子,它只有二元,我们可以理解为i被pi表示,那么pi在树上作为i的父亲,理解为i向pi连边,那么这个图就是一个内向基环树森林
对于每棵基环树把环断开,以断开的出点为根,记录根被谁表示
如何解方程呢?我们可以这么做:把树上的每一个点都用表示出根的未知数的未知数表示,只需用exgcd解出表示出根未知数的未知数,用一个未知数表示另一个未知数,用的就是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; }
pain and happy in the cruel world.