BZOJ 3779 重组病毒 LCT,树链剖分,线段树

题意:

  给一棵树,每个点一开始颜色互不相同,支持三个操作                
  1. 将一个点到根的路径染成一种新的颜色                
  2. 将一个新的点设为根,并将原来的根到这个点的路径染成一种新的颜色               
  3. 查询一个子树(对于当前根)到根的路径期望颜色数

  这道题是我迄今为止,做过的最难想,也最混乱的大数据结构题了,请诸位理智吸收。

  在我们的第一意识中,最高级的数据结构,才应该是一道题最核心的地方,但是,可这样想就会在这道题上吃瘪。

  我们观察题目,发现一开始每个点都有一个特定的颜色,而且每次修改,都是将一个点到根的路径打通。

  这就很像是LCT中的access()操作,更可观的一个性质是,LCT恰好还是一个支持换根的树形结构。

  好,这道题用肯定要LCT。

  (可是LCT并不能做这道题)

  在实际的做法中,这道题并不是以LCT为主的,换句话说,我们只是拿LCT当一个工具,来模拟操作的过程,用树链剖分协助来找到需要修改的子树以及维护子树的size,并且用线段树来维护每个点到根路径上的颜色树。

  这样,问题三就是(子树每个点到根颜色数和/子树size)。

  (那换根操作怎么办)

  凉拌!我们画一张图来看看

  假如一开始的跟是四号点(只是一个例子,和题目无关,题目里初始1为根),那么size[5]的值是7,如果将根换为7号点,如图

  我们看,现在如果我们想获得5号点的size时,只需要用n-size[7]即可(并不是因为7是跟,而是因为7是和5原深度差1的点)。

  我们多试试可以发现,换根后的子树信息有两种情况:

  首先定义一个点的深度为最原始根的条件下的深度。

  1. 要求的x点的子树信息,x点的深度比当前根的深度大,那么现在的子树信息即为原始的子树信息,这个规律在dfs序上也适用。

  2. 当x点的深度比当前根的深度小,那么就由当前跟向上跳(可以用树链剖分的跳重链的手段在log的时间复杂度内实现),跳到x点的直接儿子处,记此点为y,则现在x的子树信息即为总的树的信息减去y的子树信息(对于“子树size”和“子树点到根路径颜色数和”全都适用)。

  综上所述,其实一个点代表的子树只有两种情况,要么是原先的情况,要么是从整棵树抠出一小棵子树后的情况,而判断是哪种情况的标准,就是原始根条件下点的深度大小。此致,请注意数据结构应用的灵活性!

代码:

 

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #define ll long long
  7 #define db double
  8 #define lc(x) t[x][0]
  9 #define rc(x) t[x][1]
 10 using namespace std;
 11 const int N=100005;
 12 //树剖------------------------------------↓ 
 13 struct node{int y,nxt;}e[N*2];
 14 int n,m,ro,h[N],c=1,tp[N],pre[N],bg[N];
 15 int ed[N],tot,d[N],sz[N],son[N],pid[N];
 16 
 17 void add(int x,int y){
 18     e[++c]=(node){y,h[x]};h[x]=c;
 19     e[++c]=(node){x,h[y]};h[y]=c;
 20 } bool ispre(int x,int y){
 21     if(x==y) return 0;
 22     return bg[x]<=bg[y]&&ed[y]<=ed[x];
 23 } void dfs1(int x){
 24     sz[x]=1;
 25     for(int i=h[x],y;i;i=e[i].nxt)
 26     if((y=e[i].y)!=pre[x]){
 27         pre[y]=x;d[y]=d[x]+1;
 28         dfs1(y);sz[x]+=sz[y];
 29         if(sz[y]>sz[son[x]]) son[x]=y;
 30     } return ;
 31 } void dfs2(int x){
 32     bg[x]=++tot;pid[tot]=x;
 33     if(son[x]) 
 34     tp[son[x]]=tp[x],dfs2(son[x]);
 35     int p,y;for(int i=h[x],y;i;i=e[i].nxt)
 36     if((y=e[i].y)!=pre[x]&&y!=son[x])
 37     tp[y]=y,dfs2(y);ed[x]=tot;
 38 } int skip(int x,int y){
 39     for(;d[tp[x]]>d[y];x=pre[x]){
 40         x=tp[x];if(pre[x]==y) return x;
 41     } return pid[bg[y]+1];
 42 } ll getsz(int x){
 43     if(x==ro) return n;
 44     if(ispre(x,ro)){
 45         int y=skip(ro,x);
 46         return n-sz[y];
 47     } else return sz[x];
 48 }
 49 //树剖------------------------------------↑
 50 //线段树----------------------------------↓
 51 struct segtree{
 52     int l,r,ls,rs;ll sm,tag;
 53 }sgt[N<<2];int cnt=0,rt;
 54 void sg_pushup(int cur){
 55     int lson=sgt[cur].ls,rson=sgt[cur].rs;
 56     sgt[cur].sm=sgt[lson].sm+sgt[rson].sm;
 57 } void sg_pushdown(int cur){
 58     if(sgt[cur].tag){
 59         int ls=sgt[cur].ls,rs=sgt[cur].rs;
 60         sgt[ls].sm+=sgt[cur].tag*(sgt[ls].r-sgt[ls].l+1);
 61         sgt[rs].sm+=sgt[cur].tag*(sgt[rs].r-sgt[rs].l+1);
 62         sgt[ls].tag+=sgt[cur].tag;
 63         sgt[rs].tag+=sgt[cur].tag;sgt[cur].tag=0;
 64     } return ;
 65 } void sg_build(int l,int r,int cur){
 66     sgt[cur].l=l;sgt[cur].r=r;if(l==r){
 67         sgt[cur].ls=sgt[cur].rs=-1;
 68         sgt[cur].tag=sgt[cur].sm=0;return ;
 69     } int mid=l+r>>1;
 70     sgt[cur].ls=cnt++;sgt[cur].rs=cnt++;
 71     sg_build(l,mid,sgt[cur].ls);
 72     sg_build(mid+1,r,sgt[cur].rs);
 73     sg_pushup(cur);
 74 } void sg_upd(int l,int r,ll c,int cur){
 75     if(l<=sgt[cur].l&&sgt[cur].r<=r){
 76         sgt[cur].sm+=(sgt[cur].r-sgt[cur].l+1)*c;
 77         sgt[cur].tag+=c;return ;
 78     } int mid=sgt[cur].l+sgt[cur].r>>1;
 79     sg_pushdown(cur);
 80     if(l<=mid) sg_upd(l,r,c,sgt[cur].ls);
 81     if(mid<r) sg_upd(l,r,c,sgt[cur].rs);
 82     sg_pushup(cur);
 83 } void sg_add(int x,ll c){
 84     if(x==ro) return sg_upd(1,tot,c,rt);
 85     if(ispre(x,ro)){
 86         int y=skip(ro,x);
 87         sg_upd(bg[1],ed[1],c,rt);
 88         sg_upd(bg[y],ed[y],-c,rt);
 89     } else sg_upd(bg[x],ed[x],c,rt);
 90 } ll sg_que(int l,int r,int cur){
 91     if(l<=sgt[cur].l&&sgt[cur].r<=r)
 92     return sgt[cur].sm;ll re=0;
 93     int mid=sgt[cur].l+sgt[cur].r>>1;
 94     sg_pushdown(cur);
 95     if(l<=mid) re+=sg_que(l,r,sgt[cur].ls);
 96     if(mid<r) re+=sg_que(l,r,sgt[cur].rs);
 97     return re;
 98 } ll sg_sum(int x){
 99     if(x==ro) return sgt[rt].sm;
100     ll re=0;if(ispre(x,ro)){
101         int y=skip(ro,x);
102         re+=sg_que(bg[1],ed[1],rt);
103         re-=sg_que(bg[y],ed[y],rt);
104     } else re=sg_que(bg[x],ed[x],rt);
105     return re;
106 }
107 //线段树----------------------------------↑ 
108 //LCT-------------------------------------↓
109 int t[N][2],rev[N],lf[N],rf[N],fu[N];
110 int s[N],top=0,k,id[N];
111 void rever(int x){
112     rev[x]^=1;
113     swap(rc(x),lc(x));swap(lf[x],rf[x]);
114 } void pushup(int x){
115     lf[x]=rf[x]=id[x];
116     if(lc(x)) lf[x]=lf[lc(x)];
117     if(rc(x)) rf[x]=rf[rc(x)];
118 } void pushdown(int x){
119     int ls=lc(x),rs=rc(x);
120     if(rev[x]){
121         if(lc(x)) rever(lc(x));
122         if(rc(x)) rever(rc(x));
123     } rev[x]=0;return;
124 } bool pdrt(int x){
125     return lc(fu[x])!=x&&rc(fu[x])!=x;
126 } void rotate(int x){
127     int y=fu[x];int z=fu[y];
128     int dy=(rc(y)==x),dz=(rc(z)==y);
129     if(!pdrt(y)) t[z][dz]=x;
130     t[y][dy]=t[x][dy^1],fu[t[x][dy^1]]=y;
131     t[x][dy^1]=y;fu[y]=x;fu[x]=z;
132     pushup(y);pushup(x);
133 } void splay(int x){
134     s[++top]=x;
135     for(int i=x;!pdrt(i);i=fu[i])
136     s[++top]=fu[i];while(top)
137     pushdown(s[top--]);
138     while(!pdrt(x)){
139         int y=fu[x];int z=fu[y];if(!pdrt(y))
140         if(rc(y)==x^rc(z)==y) rotate(x);
141         else rotate(y);rotate(x);
142     } pushup(x);
143 } void access(int x){
144     for(int i=0;x;x=fu[x]){
145         splay(x);if(rc(x)) 
146         sg_add(lf[rc(x)],1);
147         if(i) sg_add(lf[i],-1);
148         rc(x)=i;pushup(x);i=x;
149     } return ;
150 } void mkrt(int x){
151     access(x),splay(x);rever(x);
152 } int fdrt(int x){
153     access(x);splay(x);
154     while(lc(x)) pushdown(x),x=lc(x);
155     return x;
156 } void split(int x,int y){
157     mkrt(x);access(y);splay(y);
158 } void link(int x,int y){
159     mkrt(x);if(fdrt(y)!=x) fu[x]=y;
160 } void cut(int x,int y){
161     mkrt(x);
162     if(fdrt(y)==x&&fu[x]==y&&!rc(x))
163     fu[x]=t[y][0]=0;pushup(y);
164 }
165 //LCT-------------------------------------↑
166 //主函数----------------------------------↓ 
167 int main(){
168     scanf("%d%d",&n,&m);
169     rt=cnt++;ro=1;
170     for(int i=1,x,y;i<n;i++)
171     scanf("%d%d",&x,&y),add(x,y);
172     pre[1]=0;d[1]=1;dfs1(1);
173     tp[1]=1;dfs2(1);
174     sg_build(1,tot,rt);
175     for(int i=1;i<=n;i++){
176         lf[i]=rf[i]=id[i]=i;
177         if(pre[i]) fu[i]=pre[i],sg_add(i,1);
178     } char s[13];int x;
179     while(m--){
180         scanf("%s%d",s,&x);
181         if(s[2]=='L') access(x),splay(x);
182         else if(s[2]=='C') mkrt(x),ro=x;
183         else{
184             ll tp1=sg_sum(x);
185             ll tp2=getsz(x);tp1+=tp2;
186             printf("%.10lf\n",(db)tp1/(db)tp2);
187         }
188     } 
189     return 0;
190 } 
数据结构杂糅

 

 

 

  

 

转载于:https://www.cnblogs.com/Alan-Luo/articles/10157121.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值