1. 程式人生 > >[Bzoj4817] [Sdoi2017]樹點塗色 (LCT神題)

[Bzoj4817] [Sdoi2017]樹點塗色 (LCT神題)

可能 維護 題目 problem through text 其中 覆蓋 int

4817: [Sdoi2017]樹點塗色


Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 629 Solved: 371
[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

題解:


其實這道題考的十分妙啊,對LCT的虛實邊性質進行了充分的利用。

我們讀完題發現操作2可以很快想到樹剖,操作3可以維護dfs序在線段樹上查詢子樹信息。 但是操作1並不是樹剖能解決的; 再來看看操作1的特點 :每次只會修改一種從未出現的顏色,並且一定是從當前點修改到根節點。 然後神奇的操作就來了 : 一開始我們按原樹建出lct,只是lct中全部都是虛邊,一個點到根節點不同 顏色數量就是它往上到根節點經過虛邊數量+1。 每次操作1相當於就是以把當前點accees上去,這樣同樣顏色的就以實邊連在一起了。是不是很神奇。。 然後再來看看怎麽統計答案,我們看看lct中什麽時候會變虛實邊關系,當然就是access了。 access時我們有一句話ch[x][1] = y ,直接把x原來的右兒子覆蓋了,變成了新的右兒子, 那麽y的虛實關系從虛轉為了實,只用找到y中最淺的一個點,對它子樹全部貢獻-1即可。 然後原來的右兒子從實變為了虛,找到它最淺的一個點,對它子樹全部貢獻+1即可。 然後我們來看看操作2,是對一條路徑進行查詢,並不能用樹剖做。 但是我們發現一個性質,因為每次修改的顏色都是新的顏色,並且是從當前點修改到根節點,設lca(u,v) != u && lca(u,v) != v 那麽u和v一定顏色不同,我們只需要在線段樹上單詢ask(u) + ask(v) - 2 * ask(lca(u,v)) + 1 即可 但為什麽要加那個1 技術分享圖片

如圖,我們查詢左下角顏色為1點和右下角的顏色為2的點;當lca顏色和它們不同時發現lca的顏色減多了,要加回來。 技術分享圖片

當lca和其中一個相同時,我們發現還是減多了,還是得加回來。

一條鏈的情況自己畫圖也是同理的。這樣對於操作2就用線段樹輕松維護了。

操作3????不就是線段樹dfs序查詢子樹嗎。

這樣我們就神奇的利用了LCT的性質把一道看似樹剖的題做成了LCT神題。。

AC代碼:

過了樣例直接交,一遍交過的酸爽
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int N = 2e5 + 12;
int st[N],ed[N],fa[N],ch[N][2],s[N << 2],la[N << 2],n,m,head[N],dt;
struct Edge{
    int to,nex;
}edge[N << 1];
void AddEdge(int u,int v)
{
    edge[++dt] = (Edge){v,head[u]};
    head[u] = dt;
}
bool isroot(int x){return ch[fa[x]][0] != x && ch[fa[x]][1] != x;}
void rotate(int x,int d)
{
    int pre = fa[x],g = fa[pre],nex = ch[x][d];
    ch[pre][d ^ 1] = nex;
    if(nex)fa[nex] = pre;
    fa[x] = g;
    if(!isroot(pre))ch[g][ch[g][1] == pre] = x;
    ch[x][d] = pre;
    fa[pre] = x;
}
void splay(int x)
{
    int pre,g;
    while(!isroot(x))
    {
        pre = fa[x],g = fa[pre];
        if(!isroot(pre) && !((ch[pre][0] == x) ^ (ch[g][0] == pre)))rotate(pre,ch[pre][0] == x);
        rotate(x,ch[pre][0] == x);
    }
}
int find(int x){while(ch[x][0])x = ch[x][0];return x;}
void push(int x){s[x] = max(s[x << 1],s[x << 1 | 1]);}
void down(int x)
{
    s[x << 1] += la[x];s[x << 1 | 1] += la[x];
    la[x << 1 | 1] += la[x];la[x << 1] += la[x];
    la[x] = 0;
}
void updata(int L,int R,int l,int r,int rt,int d)
{
    if(L <= l && r <= R){s[rt] += d;la[rt] += d;return;}
    down(rt);int mid = l + r >> 1;
    if(L <= mid)updata(L,R,l,mid,rt << 1,d);
    if(R > mid)updata(L,R,mid + 1,r,rt << 1 | 1,d);
    push(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(L <= l && r <= R)return s[rt];
    down(rt);int mid = l + r >> 1;
    if(L > mid)return Query(L,R,mid + 1,r,rt << 1 | 1);
    if(R <= mid)return Query(L,R,l,mid,rt << 1);
    return max(Query(L,R,l,mid,rt << 1),Query(L,R,mid + 1,r,rt << 1 | 1));
}
int ask(int x){return Query(st[x],st[x],1,n,1);}
void access(int x)
{
   int y = 0,t;
   while(x)
  {
   splay(x);
   if(t = find(ch[x][1]))updata(st[t],ed[t],1,n,1,1);
   ch[x][1] = y;if(t = find(y))updata(st[t],ed[t],1,n,1,-1);
   y = x;x = fa[x];
  }
}
int hson[N],sz[N],tot,top[N],dep[N],Fa[N],id[N];
void dfs(int u)
{
    sz[u] = 1;
    for(int i = head[u];i;i = edge[i].nex)
    {
        if(sz[edge[i].to])continue;
        Fa[edge[i].to] = u;
        dep[edge[i].to] = dep[u] + 1;
        dfs(edge[i].to);
        sz[u] += sz[edge[i].to];
        if(sz[hson[u]] < sz[edge[i].to])hson[u] = edge[i].to;
    }
}
void dfs(int u,int tp)
{
    top[u] = tp;st[u] = ++tot;id[tot] = u;
    if(hson[u])dfs(hson[u],tp);
    for(int i = head[u];i;i = edge[i].nex)
    if(!st[edge[i].to])dfs(edge[i].to,edge[i].to);
    ed[u] = tot;
}
int lca(int x,int y)
{
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]])swap(x,y);
        x = Fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
int Q1(int u,int v){return ask(u) + ask(v) - 2 * ask(lca(u,v)) + 1;}
int Q2(int u){return Query(st[u],ed[u],1,n,1);}
int main()
{
       scanf("%d %d",&n,&m);int tp,x,y;
       for(int i = 1;i < n;i++)
       {
           scanf("%d %d",&x,&y);
           AddEdge(x,y);AddEdge(y,x);
       }
       dfs(1);dfs(1,1);
       for(int i = 1;i <= n;i++)updata(st[i],ed[i],1,n,1,1),fa[i] = Fa[i];
       while(m--)
       {
           scanf("%d",&tp);
           if(tp == 1)scanf("%d",&x),access(x);
           if(tp == 2)scanf("%d %d",&x,&y),printf("%d\n",Q1(x,y));
           if(tp == 3)scanf("%d",&x),printf("%d\n",Q2(x));
       }
}

[Bzoj4817] [Sdoi2017]樹點塗色 (LCT神題)