[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

Posted HocRiser

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)相关的知识,希望对你有一定的参考价值。

4817: [Sdoi2017]树点涂色

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 692  Solved: 408
[Submit][Status][Discuss]

Description

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x y:
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作

Input

第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000

Output

每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值

Sample Input

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

Sample Output

3
4
2
2

HINT

Source

[Submit][Status][Discuss]

首先发现第一个操作很像LCT里的Access(),这个方向已经对了。

我们设f[x]表示x与父节点的值是否一样,且f[1]=1,那么对于每个点的答案就是这个点到根上的所有f[x]之和,设为g[x]。

2操作可以转化成g[x]+g[y]-2*g[lca]+1(这里的+1要考虑清楚),3操作可以转化成求x子树中的g[]的最大值,考虑如何维护g[]。

可以发现从x修改到根时,只有每次发生链的切换的时候f[]值才会改变,而每次链的切换正是LCT中Access()所执行的操作,所以我们每次Access()切换链的时候,将其子树的所有g[]加1,对新接进来的节点的子树g[]减1,这个涉及子树的操作,直接DFS+线段树解决即可。

写代码的时候突然发现自己不会区间修改区间查询的线段树了。。吃枣药丸。。

说一下标记永久化的事情,一般的标记是存放自顶向下的懒惰信息,标记自顶向下逐层下放,标记永久化则是可以看作存的是仅在这一层的懒惰信息,标记并不下放而是自底向上地合并。

一般来说标记永久化可能会短一点,速度也会快一些,但并不是非常直观,所以在不卡常数的情况下还是写普通标记吧。

先贴一份没有标记永久化的片段

void push(int x,int L,int R){
    if (!tag[x]) return;
    tag[ls]+=tag[x]; mx[ls]+=tag[x];
    tag[rs]+=tag[x]; mx[rs]+=tag[x];
    tag[x]=0;
}

void ins(int x,int L,int R,int l,int r,int k){
    if (L==l && r==R){ tag[x]+=k; mx[x]+=k; return; }
    int mid=(L+R)>>1; push(x,L,R);
    if (r<=mid) ins(ls,L,mid,l,r,k);
    else if (l>mid) ins(rs,mid+1,R,l,r,k);
        else ins(ls,L,mid,l,mid,k),ins(rs,mid+1,R,mid+1,r,k);
    mx[x]=max(mx[ls],mx[rs]);
}

int ask(int x,int L,int R,int l,int r){
    if (L==l && r==R) return mx[x];
    int mid=(L+R)>>1; push(x,L,R);
    if (r<=mid) return ask(ls,L,mid,l,r);
    else if (l>mid) return ask(rs,mid+1,R,l,r);
        else return max(ask(ls,L,mid,l,mid),ask(rs,mid+1,R,mid+1,r));
}

 

下面是标记永久化的程序,比原来快1/5。

 

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define ls (x<<1)
  4 #define rs ((x<<1)|1)
  5 #define rep(i,l,r) for (int i=l; i<=r; i++)
  6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
  7 using namespace std;
  8 
  9 const int N=400100;
 10 int n,m,x,y,op,cnt,nd,tim,val[N],top[N],son[N],to[N],h[N],nxt[N];
 11 int sz[N],d[N],L[N],R[N],ch[N][2],mx[N],tag[N],f[N],fa[N];
 12 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
 13 bool isroot(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); }
 14 
 15 void dfs(int x,int pre){
 16     sz[x]=1; d[x]=d[pre]+1;
 17     For(i,x) if ((k=to[i])!=pre){
 18         f[k]=fa[k]=x; dfs(k,x); sz[x]+=sz[k];
 19         if (sz[son[x]]<sz[k]) son[x]=k;
 20     }
 21 }
 22 
 23 void dfs2(int x,int tp){
 24     L[x]=++tim; top[x]=tp; val[tim]=d[x];
 25     if (son[x]) dfs2(son[x],tp);
 26     For(i,x) if ((k=to[i])!=fa[x] && k!=son[x]) dfs2(k,k);
 27     R[x]=tim;
 28 }
 29 
 30 void rot(int x){
 31     int y=f[x],z=f[y],w=ch[y][1]==x;
 32     if (!isroot(y)) ch[z][ch[z][1]==y]=x;
 33     f[x]=z; f[y]=x; f[ch[x][w^1]]=y;
 34     ch[y][w]=ch[x][w^1]; ch[x][w^1]=y;
 35 }
 36 
 37 void splay(int x){
 38     while (!isroot(x)){
 39         int y=f[x],z=f[y];
 40         if (!isroot(y)){
 41             if ((ch[z][1]==y)^(ch[y][1]==x)) rot(x); else rot(y);
 42         }
 43         rot(x);
 44     }
 45 }
 46 
 47 void ins(int x,int L,int R,int l,int r,int k){
 48     if (L==l && r==R){ tag[x]+=k; mx[x]+=k; return; }
 49     int mid=(L+R)>>1;
 50     if (r<=mid) ins(ls,L,mid,l,r,k);
 51     else if (l>mid) ins(rs,mid+1,R,l,r,k);
 52         else ins(ls,L,mid,l,mid,k),ins(rs,mid+1,R,mid+1,r,k);
 53     mx[x]=max(mx[ls],mx[rs])+tag[x];
 54 }
 55 
 56 int ask(int x,int L,int R,int l,int r){
 57     if (L==l && r==R) return mx[x];
 58     int mid=(L+R)>>1;
 59     if (r<=mid) return tag[x]+ask(ls,L,mid,l,r);
 60     else if (l>mid) return tag[x]+ask(rs,mid+1,R,l,r);
 61         else return tag[x]+max(ask(ls,L,mid,l,mid),ask(rs,mid+1,R,mid+1,r));
 62 }
 63 
 64 int find(int x){ while (ch[x][0]) x=ch[x][0]; return x; }
 65 
 66 void access(int x){
 67     for (int y=0; x; y=x,x=f[x]){
 68         splay(x); int t=find(ch[x][1]);
 69         if (t) ins(1,1,n,L[t],R[t],1);
 70         t=find(y); ch[x][1]=y;
 71         if (t) ins(1,1,n,L[t],R[t],-1);
 72     }
 73 }
 74 
 75 void build(int x,int L,int R){
 76     if (L==R) { mx[x]=val[L]; return; }
 77     int mid=(L+R)>>1;
 78     build(ls,L,mid); build(rs,mid+1,R);
 79     mx[x]=max(mx[ls],mx[rs]);
 80 }
 81 
 82 int get(int u,int v){
 83     for (; top[u]!=top[v]; u=fa[top[u]])
 84         if (d[top[u]]<d[top[v]]) swap(u,v);
 85     return (d[u]<d[v])?u:v;
 86 }
 87 
 88 int main(){
 89     freopen("paint.in","r",stdin);
 90     freopen("paint.out","w",stdout);
 91     scanf("%d%d",&n,&m);
 92     rep(i,2,n) scanf("%d%d",&x,&y),add(x,y),add(y,x);
 93     dfs(1,0); dfs2(1,1); build(1,1,n);
 94     while (m--){
 95         scanf("%d",&op);
 96         if (op==1) scanf("%d",&x),access(x);
 97         else if (op==2){
 98             scanf("%d%d",&x,&y); int lca=get(x,y);
 99             printf("%d\n",ask(1,1,n,L[x],L[x])+ask(1,1,n,L[y],L[y])-2*ask(1,1,n,L[lca],L[lca])+1);
100         }else scanf("%d",&x),printf("%d\n",ask(1,1,n,L[x],R[x]));
101     }
102     return 0;
103 }

 

以上是关于[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)的主要内容,如果未能解决你的问题,请参考以下文章

[Bzoj4817] [Sdoi2017]树点涂色 (LCT神题)

bzoj4817: [Sdoi2017]树点涂色

BZOJ4817 [Sdoi2017]树点涂色

bzoj4817 [Sdoi2017]树点涂色

AC日记——[SDOI2017]树点涂色 bzoj 4817

[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)