BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)

题目描述

Bob有一棵 nn 个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。

定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。

Bob可能会进行这几种操作:

  • 1 x

把点 xx 到根节点的路径上所有的点染上一种没有用过的新颜色。

  • 2 x y

求 xx 到 yy 的路径的权值。

  • 3 x

在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行 mm 次操作

输入输出格式

输入格式:

 

第一行两个数 n,mn,m 。

接下来 n-1n1 行,每行两个数 a,ba,b ,表示 aa 与 bb 之间有一条边。

接下来 mm 行,表示操作,格式见题目描述

 

输出格式:

 

每当出现2,3操作,输出一行。

如果是2操作,输出一个数表示路径的权值

如果是3操作,输出一个数表示权值的最大值

 

输入输出样例

输入样例#1: 复制
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
输出样例#1: 复制
3
4
2
2

说明

共10个测试点

测试点1, 1\leq n,m\leq10001n,m1000

测试点2、3,没有2操作

测试点4、5,没有3操作

测试点6,树的生成方式是,对于 i(2\leq i \leq n)i(2in) ,在1到 i-1i1 中随机选一个点作为i的父节点。

测试点7, 1\leq n,m\leq 500001n,m50000

测试点8, 1\leq n \leq 500001n50000

测试点9,10,无特殊限制

对所有数据, 1\leq n \leq 10^51n105 , 1\leq m \leq 10^51m105

时间限制:1s

空间限制:128MB

题解

照例先膜一发大佬

说真的是道好题,LCT,树剖,dfs序,线段树都得用上

然后抄题解抄得不亦乐乎悲催的没有发现一个地方我和大佬思路是完全不一样的……

后来因为这个地方调了整整一个小时……我少了一个小时的睡觉时间!!!

先讲个思路……

操作一

因为颜色都不一样,也没有必要维护颜色了

然后我们能发现,任何时候同一个颜色必定是一条链,而且深度严格递增

很显然,因为每一次颜色修改只会在到根的路径上进行

所以每一个颜色都可以用LCT中的splay来维护了

并且我们发现,操作一不就是LCT中的access么?

操作二

每一个颜色都在一个splay中

所以一条路径上的颜色数量就是跨过了几个splay,也就是经过了几条虚边

于是split就好了显然是行不通的,因为因为乱搞会破坏掉关系

然后可以考虑用树上差分

$f[x]+f[y]-2*f[lca(x,y)]+1$就是这条路径上的颜色数量了(lca被减了两次要加回来)(f表示到根节点的虚边数量)

然后考虑怎么维护$f$

刚开始时是节点的深度

然后显然access的时候会对$f$有影响,一条边变虚,一条边变实

所以只要把原来的虚边指向的子树答案全部+1,实边指向的子树答案全部-1

这就是一个子树操作啦

用线段树+dfs序,可以很轻松的完成子树操作

操作三

查询其实和修改差不多

直接线段树查询即可

ps:写的树剖求LCA,结果因为原树和splay都要维护father,看了看大佬的板子只用了一个数组,结果莫名其妙T到死……算了滚去睡觉了(¦3[▓▓]实在太困了……

  1 //minamoto
  2 #include<iostream>
  3 #include<cstdio>
  4 using std::swap;
  5 using std::max;
  6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
  7 char buf[1<<21],*p1=buf,*p2=buf;
  8 inline int read(){
  9     #define num ch-'0'
 10     char ch;bool flag=0;int res;
 11     while(!isdigit(ch=getc()))
 12     (ch=='-')&&(flag=true);
 13     for(res=num;isdigit(ch=getc());res=res*10+num);
 14     (flag)&&(res=-res);
 15     #undef num
 16     return res;
 17 }
 18 char obuf[1<<24],*o=obuf;
 19 inline void print(int x){
 20     if(x>9) print(x/10);
 21     *o++=x%10+48;
 22 }
 23 const int N=100005,M=N*20;
 24 int fa[N],f[N],ch[N][2],sz[N],son[N],dfn[N],rk[N];
 25 int dep[N],top[N],L[M],R[M],mx[M],lz[M],rs[N],mid[M];
 26 int head[N],Next[N<<1],ver[N<<1];
 27 int n,m,cnt,num,tot;
 28 inline void add(int u,int v){
 29     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
 30     ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
 31 }
 32 void dfs1(int u){
 33     sz[u]=1,dep[u]=dep[f[u]]+1;
 34     dfn[u]=++num,rk[num]=u;
 35     for(int i=head[u];i;i=Next[i]){
 36         int v=ver[i];
 37         if(v==f[u]) continue;
 38         fa[v]=f[v]=u;
 39         dfs1(v);
 40         sz[u]+=sz[v];
 41         if(!son[u]||sz[v]>sz[son[u]]) son[u]=v;
 42     }
 43     rs[u]=num;
 44 }
 45 void dfs2(int u){
 46     if(!top[u]) top[u]=u;
 47     if(!son[u]) return;
 48     top[son[u]]=top[u],dfs2(son[u]);
 49     for(int i=head[u];i;i=Next[i]){
 50         int v=ver[i];
 51         if(v!=f[u]&&v!=son[u]) dfs2(v);
 52     }
 53 }
 54 inline int LCA(int u,int v){
 55     while(top[u]!=top[v])
 56     dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]];
 57     return dep[u]<dep[v]?u:v;
 58 }
 59 inline void build(int p,int l,int r){
 60     L[p]=l,R[p]=r,mid[p]=(l+r)>>1;
 61     if(l==r)return (void)(mx[p]=dep[rk[l]]);
 62     build(p<<1,l,mid[p]),build(p<<1|1,mid[p]+1,r);
 63     mx[p]=max(mx[p<<1],mx[p<<1|1]);
 64 }
 65 #define pushdown if(lz[p]) update(p<<1,L[p],mid[p],lz[p]),update(p<<1|1,mid[p]+1,R[p],lz[p]),lz[p]=0
 66 void update(int p,int l,int r,int v){
 67     if(L[p]==l&&R[p]==r){mx[p]+=v,lz[p]+=v;return;}
 68     pushdown;
 69     if(r<=mid[p]) update(p<<1,l,r,v);
 70     else if(l>mid[p]) update(p<<1|1,l,r,v);
 71     else update(p<<1,l,mid[p],v),update(p<<1|1,mid[p]+1,r,v);
 72     mx[p]=max(mx[p<<1],mx[p<<1|1]);
 73 }
 74 int get(int v){
 75     int p=1;
 76     while(L[p]!=R[p]){
 77         pushdown;p=(p<<1)+(v>mid[p]);
 78         /*pushdown后面逗号竟然死循环了……*/
 79     }
 80     return mx[p];
 81 }
 82 int ask(int p,int l,int r){
 83     if(L[p]==l&&R[p]==r) return mx[p];
 84     pushdown;
 85     if(r<=mid[p]) return ask(p<<1,l,r);
 86     if(l>mid[p]) return ask(p<<1|1,l,r);
 87     return max(ask(p<<1,l,mid[p]),ask(p<<1|1,mid[p]+1,r));
 88 }
 89 #undef pushdown
 90 bool isroot(int x){
 91     return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
 92 }
 93 void rotate(int x){
 94     int y=fa[x],z=fa[y],d=ch[y][1]==x;
 95     if(!isroot(y)) ch[z][ch[z][1]==y]=x;
 96     fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y;
 97 }
 98 void splay(int x){
 99     for(int y=fa[x];!isroot(x);y=fa[x]){
100         if(!isroot(y))
101         rotate(x);
102         rotate(x);
103     }
104 }
105 int findroot(int x){
106     while(ch[x][0]) x=ch[x][0];
107     return x;
108 }
109 void access(int x){
110     for(int w,y=0;x;x=fa[y=x]){
111         splay(x);
112         if(ch[x][1]) w=findroot(ch[x][1]),update(1,dfn[w],rs[w],1);
113         if((ch[x][1]=y)) w=findroot(y),update(1,dfn[w],rs[w],-1);
114         /*把原来的子树变为虚的,整个子树的答案+1
115         然后新的子树答案-1*/
116     }
117 }
118 int main(){
119     //freopen("testdata.in","r",stdin);
120     int n=read(),m=read();
121     for(int i=1;i<n;++i){
122         int u=read(),v=read();
123         add(u,v);
124     }
125     dfs1(1),dfs2(1),build(1,1,n);
126     while(m--){
127         int opt=read(),x=read();
128         switch(opt){
129             case 1:{
130                 access(x);
131                 break;
132             }
133             case 2:{
134                 int y=read();
135                 print(get(dfn[x])+get(dfn[y])-get(dfn[LCA(x,y)])*2+1),*o++='\n';
136                 break;
137             }
138             case 3:{
139                 print(ask(1,dfn[x],rs[x])),*o++='\n';
140                 break;
141             }
142         }
143     }
144     fwrite(obuf,o-obuf,1,stdout);
145     return 0;
146 }

 

posted @ 2018-08-04 23:24  bztMinamoto  阅读(204)  评论(0编辑  收藏  举报
Live2D