洛谷 P1501 [国家集训队]Tree II

看来这个LCT板子并没有什么问题

  1 #include<cstdio>
  2 #include<algorithm>
  3 using namespace std;
  4 typedef long long LL;
  5 const LL md=51061;
  6 namespace LCT
  7 {
  8 struct Node
  9 {
 10     Node *ch[2],*fa;
 11     bool rev;
 12     LL addv,mulv;
 13     LL dat,sum,sz;
 14     void padd(LL x)
 15     {
 16         addv=(addv+x)%md;dat=(dat+x)%md;sum=(sum+x*sz)%md;
 17     }
 18     void pmul(LL x)
 19     {
 20         addv=addv*x%md;mulv=mulv*x%md;dat=dat*x%md;sum=sum*x%md;
 21     }
 22     void upd()
 23     {
 24         sum=((ch[0]?ch[0]->sum:0)+(ch[1]?ch[1]->sum:0)+dat)%md;
 25         sz=(ch[0]?ch[0]->sz:0)+(ch[1]?ch[1]->sz:0)+1;
 26     }
 27     void pd()
 28     {
 29         if(rev)
 30         {
 31             swap(ch[0],ch[1]);
 32             if(ch[0])    ch[0]->rev^=1;
 33             if(ch[1])    ch[1]->rev^=1;
 34             rev=0;
 35         }
 36         if(mulv!=1)
 37         {
 38             if(ch[0])    ch[0]->pmul(mulv);
 39             if(ch[1])    ch[1]->pmul(mulv);
 40             mulv=1;
 41         }
 42         if(addv)
 43         {
 44             if(ch[0])    ch[0]->padd(addv);
 45             if(ch[1])    ch[1]->padd(addv);
 46             addv=0;
 47         }
 48     }
 49 }nodes[300100];
 50 LL mem;
 51 Node *getnode()
 52 {
 53     return nodes+(mem++);
 54 }
 55 bool isroot(Node *x)
 56 {
 57     return (!x->fa)||((x->fa->ch[0]!=x)&&(x->fa->ch[1]!=x));
 58 }
 59 bool gson(Node *o)    {return o==o->fa->ch[1];}//获得是父亲的左儿子(返回0)还是右儿子(1),要求保证存在父亲
 60 void rotate(Node *o,bool d)
 61 //在o子树中执行d=0左旋,d=1右旋,在旋转前不标记下传,并将o父节点的对应子节点由o变为需要值,要求保证存在子树(!d)
 62 {
 63     Node *k=o->ch[!d];if(!isroot(o))    o->fa->ch[gson(o)]=k;//注意这一句修改o父节点的要写在前面,曾经出过错调了一会
 64     o->ch[!d]=k->ch[d];k->ch[d]=o;
 65     o->upd();k->upd();
 66     k->fa=o->fa;o->fa=k;if(o->ch[!d])    o->ch[!d]->fa=o;
 67 }
 68 Node *st[300100];LL top;
 69 void solvetag(Node *o)
 70 {
 71     while(!isroot(o))    st[++top]=o,o=o->fa;
 72     st[++top]=o;
 73     while(top)    st[top--]->pd();
 74 }
 75 void splay(Node *o)
 76 {
 77     solvetag(o);
 78     Node *fa,*fafa;bool d1,d2;
 79     while(!isroot(o))
 80     {
 81         fa=o->fa;d1=(o==fa->ch[0]);
 82         if(isroot(fa))    rotate(fa,d1);
 83         else
 84         {
 85             fafa=o->fa->fa;d2=(fa==fafa->ch[0]);//要保证fa不是root之后才能获取这两个值,曾错过
 86             if(d1==d2)    rotate(fafa,d1),rotate(fa,d1);//zig-zig,两次相同方向的单旋,先把父亲转上去,再把自己转上去
 87             else    rotate(fa,d1),rotate(fafa,d2);//zig-zag,两次相反方向的单旋,连续两次把自己转上去
 88         }
 89     }
 90 }
 91 void access(Node *o)
 92 {
 93     for(Node *lst=NULL;o;lst=o,o=o->fa)
 94     {
 95         splay(o);//此处不pushdown是由于splay中保证进行过了
 96         o->ch[1]=lst;o->upd();//注意upd
 97     }
 98 }
 99 Node *gtop(Node *o)
100 {
101     access(o);splay(o);
102     for(;o->ch[0];o=o->ch[0],o->pd());//此处不在开始前pushdown(o)是由于splay中保证进行过了
103     splay(o);return o;//听说这里不splay一下也很难卡掉
104 }
105 void mtop(Node *o)    {access(o);splay(o);o->rev^=1;}
106 void link(Node *x,Node *y)
107 {
108     if(gtop(x)==gtop(y))    return;
109     mtop(y);y->fa=x;
110 }
111 void cut(Node *x,Node *y)
112 {
113     mtop(x);access(y);splay(y);
114     if(y->ch[0]!=x||x->ch[1])    return;//如果x、y之间直接有边,那么上面一行的操作之后应当是x与y在单独一棵splay中,那么一定保证y左子节点是x且x没有右子节点
115     x->fa=y->ch[0]=NULL;//注意,改的是x的父亲和y的子节点(虽然x的确是树的根,但是此时在splay上是y的子节点,不能搞混)
116     y->upd();//注意
117 }
118 LL query(Node *x,Node *y)
119 {
120     mtop(x);access(y);splay(y);
121     //if(gtop(y)!=x)    return 0;//此题保证x与y连通,不需要
122     return y->sum;
123 }
124 void add(Node *x,Node *y,LL t)
125 {
126     mtop(x);access(y);splay(y);
127     y->padd(t);
128 }
129 void mul(Node *x,Node *y,LL t)
130 {
131     mtop(x);access(y);splay(y);
132     y->pmul(t);
133 }
134 }
135 LCT::Node *nd[300100];
136 LL n,q;char tmp[20];
137 int main()
138 {
139     LL i,x,y,t,x2,y2;
140     scanf("%lld%lld",&n,&q);
141     for(i=1;i<=n;i++)
142     {
143         nd[i]=LCT::getnode();
144         nd[i]->mulv=1;nd[i]->dat=nd[i]->sum=1;nd[i]->sz=1;
145     }
146     for(i=1;i<n;i++)
147     {
148         scanf("%lld%lld",&x,&y);
149         LCT::link(nd[x],nd[y]);
150     }
151     while(q--)
152     {
153         scanf("%s",tmp);
154         switch(tmp[0])
155         {
156         case ‘+‘:
157             scanf("%lld%lld%lld",&x,&y,&t);
158             LCT::add(nd[x],nd[y],t);
159             break;
160         case ‘-‘:
161             scanf("%lld%lld%lld%lld",&x,&y,&x2,&y2);
162             LCT::cut(nd[x],nd[y]);LCT::link(nd[x2],nd[y2]);
163             break;
164         case ‘*‘:
165             scanf("%lld%lld%lld",&x,&y,&t);
166             LCT::mul(nd[x],nd[y],t);
167             break;
168         case ‘/‘:
169             scanf("%lld%lld",&x,&y);
170             printf("%lld\n",LCT::query(nd[x],nd[y]));
171         }
172     }
173     return 0;
174 }

压行后:

  1 #pragma GCC optimize("Ofast")
  2 #pragma GCC optimize("inline","fast-math","unroll-loops","no-stack-protector")
  3 #pragma GCC diagnostic error "-fwhole-program"
  4 #pragma GCC diagnostic error "-fcse-skip-blocks"
  5 #pragma GCC diagnostic error "-funsafe-loop-optimizations"
  6 #pragma GCC diagnostic error "-std=c++14"
  7 #include<cstdio>
  8 #include<algorithm>
  9 using namespace std;
 10 typedef long long LL;
 11 const LL md=51061;
 12 namespace LCT
 13 {
 14 struct Node
 15 {
 16     Node *ch[2],*fa;
 17     bool rev;
 18     LL addv,mulv;
 19     LL dat,sum,sz;
 20     void padd(LL x)
 21     {
 22         addv=(addv+x)%md;dat=(dat+x)%md;sum=(sum+x*sz)%md;
 23     }
 24     void pmul(LL x)
 25     {
 26         addv=addv*x%md;mulv=mulv*x%md;dat=dat*x%md;sum=sum*x%md;
 27     }
 28     void upd()
 29     {
 30         sum=((ch[0]?ch[0]->sum:0)+(ch[1]?ch[1]->sum:0)+dat)%md;
 31         sz=(ch[0]?ch[0]->sz:0)+(ch[1]?ch[1]->sz:0)+1;
 32     }
 33     void pd()
 34     {
 35         if(rev)
 36         {
 37             swap(ch[0],ch[1]);
 38             if(ch[0])    ch[0]->rev^=1;
 39             if(ch[1])    ch[1]->rev^=1;
 40             rev=0;
 41         }
 42         if(mulv!=1)
 43         {
 44             if(ch[0])    ch[0]->pmul(mulv);
 45             if(ch[1])    ch[1]->pmul(mulv);
 46             mulv=1;
 47         }
 48         if(addv)
 49         {
 50             if(ch[0])    ch[0]->padd(addv);
 51             if(ch[1])    ch[1]->padd(addv);
 52             addv=0;
 53         }
 54     }
 55 }nodes[300100];
 56 LL mem;
 57 Node *getnode()
 58 {
 59     return nodes+(mem++);
 60 }
 61 bool isroot(Node *x)
 62 {
 63     return (!x->fa)||((x->fa->ch[0]!=x)&&(x->fa->ch[1]!=x));
 64 }
 65 bool gson(Node *o)    {return o==o->fa->ch[1];}//获得是父亲的左儿子(返回0)还是右儿子(1),要求保证存在父亲
 66 void rotate(Node *o,bool d)
 67 //在o子树中执行d=0左旋,d=1右旋,在旋转前不标记下传,并将o父节点的对应子节点由o变为需要值,要求保证存在子树(!d)
 68 {
 69     Node *k=o->ch[!d];if(!isroot(o))    o->fa->ch[gson(o)]=k;//注意这一句修改o父节点的要写在前面,曾经出过错调了一会
 70     o->ch[!d]=k->ch[d];k->ch[d]=o;
 71     o->upd();k->upd();
 72     k->fa=o->fa;o->fa=k;if(o->ch[!d])    o->ch[!d]->fa=o;
 73 }
 74 Node *st[300100];LL top;
 75 void solvetag(Node *o)
 76 {
 77     while(!isroot(o))    st[++top]=o,o=o->fa;
 78     st[++top]=o;
 79     while(top)    st[top--]->pd();
 80 }
 81 void splay(Node *o)
 82 {
 83     solvetag(o);
 84     Node *fa,*fafa;bool d1,d2;
 85     while(!isroot(o))
 86     {
 87         fa=o->fa;d1=(o==fa->ch[0]);
 88         if(isroot(fa))    rotate(fa,d1);
 89         else
 90         {
 91             fafa=o->fa->fa;d2=(fa==fafa->ch[0]);//要保证fa不是root之后才能获取这两个值,曾错过
 92             if(d1==d2)    rotate(fafa,d1),rotate(fa,d1);//zig-zig,两次相同方向的单旋,先把父亲转上去,再把自己转上去
 93             else    rotate(fa,d1),rotate(fafa,d2);//zig-zag,两次相反方向的单旋,连续两次把自己转上去
 94         }
 95     }
 96 }
 97 void access(Node *o)
 98 {
 99     for(Node *lst=NULL;o;lst=o,o=o->fa)
100     {
101         splay(o);
102         o->ch[1]=lst;o->upd();
103     }
104 }
105 Node *gtop(Node *o)
106 {
107     access(o);splay(o);
108     for(;o->ch[0];o=o->ch[0],o->pd());
109     splay(o);return o;
110 }
111 void mtop(Node *o)                {access(o);splay(o);o->rev^=1;}
112 void split(Node *x,Node *y)        {mtop(x);access(y);splay(y);}
113 void link(Node *x,Node *y)        {mtop(y);y->fa=x;}
114 void cut(Node *x,Node *y)        {split(x,y);x->fa=y->ch[0]=NULL;y->upd();}
115 LL query(Node *x,Node *y)        {split(x,y);return y->sum;}
116 void add(Node *x,Node *y,LL t)    {split(x,y);y->padd(t);}
117 void mul(Node *x,Node *y,LL t)    {split(x,y);y->pmul(t);}
118 }
119 LCT::Node *nd[300100];
120 LL n,q;char tmp[20];
121 int main()
122 {
123     LL i,x,y,t,x2,y2;
124     scanf("%lld%lld",&n,&q);
125     for(i=1;i<=n;i++)
126     {
127         nd[i]=LCT::getnode();
128         nd[i]->mulv=1;nd[i]->dat=nd[i]->sum=1;nd[i]->sz=1;
129     }
130     for(i=1;i<n;i++)
131     {
132         scanf("%lld%lld",&x,&y);
133         LCT::link(nd[x],nd[y]);
134     }
135     while(q--)
136     {
137         scanf("%s",tmp);
138         switch(tmp[0])
139         {
140         case ‘+‘:
141             scanf("%lld%lld%lld",&x,&y,&t);
142             LCT::add(nd[x],nd[y],t);
143             break;
144         case ‘-‘:
145             scanf("%lld%lld%lld%lld",&x,&y,&x2,&y2);
146             LCT::cut(nd[x],nd[y]);LCT::link(nd[x2],nd[y2]);
147             break;
148         case ‘*‘:
149             scanf("%lld%lld%lld",&x,&y,&t);
150             LCT::mul(nd[x],nd[y],t);
151             break;
152         case ‘/‘:
153             scanf("%lld%lld",&x,&y);
154             printf("%lld\n",LCT::query(nd[x],nd[y]));
155         }
156     }
157     return 0;
158 }

原文地址:https://www.cnblogs.com/hehe54321/p/8877033.html

时间: 2024-10-11 07:05:24

洛谷 P1501 [国家集训队]Tree II的相关文章

洛谷P1501 [国家集训队]Tree II

关于LCT的其它问题可以参考一下我的LCT总结 一道LCT很好的练习放懒标记技巧的题目. 一开始看到又做加法又做乘法的时候我是有点mengbi的. 然后我想起了模板线段树2......(相信各位Dalao一定做过这道题) 这里的维护懒标记方法很像.除了翻转标记以外还要维护乘法标记和加法标记. 根据运算优先级,乘法是要先算的,所以先放,放的时候子树的\(sum\),乘法标记,加法标记,儿子的\(val\)统统都要乘一遍. 放加法标记的时候,想到线段树的区间大小是稳定的,而Splay并不是,所以还要

【刷题】洛谷 P1501 [国家集训队]Tree II

题目描述 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一: + u v c:将u到v的路径上的点的权值都加上自然数c: - u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树: * u v c:将u到v的路径上的点的权值都乘上自然数c: / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数. 输入输出格式 输入格式: 第一行两个整数n,q 接下来n-1行每行两个正整数u,v,

洛谷P1501 [国家集训队]Tree II【LCT】

题目描述 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一: u v c:将u到v的路径上的点的权值都加上自然数c: u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树: u v c:将u到v的路径上的点的权值都乘上自然数c: / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数. 输入格式: 第一行两个整数n,q 接下来n-1行每行两个正整数u,v,描述这棵树 接下来q行,每

P1501 [国家集训队]Tree II

做法 P1501 [国家集训队]Tree II 树上懒惰标记维护动态路径模板题 做法 其实做这题也能练一下对\(LCT\)的了解 我们对\(x,y\)这条路径修改时:\(Split(x,y);....(y)\),传到\(y\)上去就行了 我们发现不管什么操作都会用到\(Access\),其中把底下的点上旋\(splay\)的同时会把\(y\)上的标记取下来 和线段树差不多吧不知道为什么是道黑题 My complete code 写代码(15min)+调试(15min)感觉比模板还容易打 #inc

洛谷 1775. [国家集训队2010]小Z的袜子

1775. [国家集训队2010]小Z的袜子 ★★★   输入文件:hose.in   输出文件:hose.out   简单对比时间限制:1 s   内存限制:512 MB [题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,

题解 luogu P1501【[国家集训队]Tree II】(Link-Cut-Tree)

Link-Cut-Tree 的懒标记下传正确食用方法. 我们来逐步分析每一个操作. 1:+ u v c:将u到v的路径上的点的权值都加上自然数c; 解决方法: 很显然,我们可以 split(u,v) 来提取u,v这一段区间,提取完了将 Splay(v),然后直接在v上打加法标记add即可. 代码: inline void pushadd(ll x,ll val){//打标记 s[x]+=sz[x]*val,v[x]+=val,add[x]+=val; s[x]%=MOD,v[x]%=MOD,ad

[国家集训队]Tree II

题目 这不是线段树模板2放\(lct\)上了吗 于是开始码码码 之后一直wawawa 于是开始调调调 之后旁边的慎老师看了一看代码就说,你下放乘法标记的时候不乘加法标记吗 我:... 代码 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define maxn 300005 #define re register #define LL long long #

洛谷P1182 数列分段Section II 二分答案

洛谷P1182 数列分段Section II 二分答案 题意:将 n 个 数 分为 m段 求一种方案,使这m段中最大的和 最小 额..可能有点拗口,其实就是说每一种方案,都有对应的 每段和的最大值,要求一种方案,使最大值最小 题解 :二分答案 mid为分成的最大值, 然后O(n) 判断 答案 是否可行 贪心的做下去,如果再加上就要超过了,那就新开一段 最后判断开的段数是否小于 m 1.注意要判断 如果当前这个值大于 mid,一个值就已经大于 mid了,那就直接退出了,否则 ,这个值也只会单独算为

洛谷1288 取数游戏II 博弈论

洛谷1288 取数游戏II 博弈论 最优策略 一定是你一步把值走完,然后我再走完,这样不给别人留后路 然后这样走 只要自己从左走 或者从右走其中有一个有奇数步可走,则说明是必胜局 如果都是只能走偶数步的,就是必败局 . 另一个题解 首先,对于一条链a1,a2,a3,a4......0 如果是偶数条边,那么现手一定赢,因为他每一次都只用把后面一条取完,例如 5 4 3 6 5 0 先手取完5,后手没法回到前一个位置,而无论接下来后手去多少,先手继续取完3,再然后取完5,后手没办法再去,先手赢.就这