題目鏈接:點(diǎn)擊查看
題目大意:給出一棵 n 個(gè)節(jié)點(diǎn)構(gòu)成的數(shù),每個(gè)節(jié)點(diǎn)都有一個(gè)顏色,現(xiàn)在需要執(zhí)行 m 次操作,每次操作分為如下兩種類型:
Q x:回答所有顏色為 x 的節(jié)點(diǎn)構(gòu)成的連通子圖含有的最少邊數(shù) U x y:將點(diǎn) x 的顏色修改為 y
題目分析:首先轉(zhuǎn)換模型,要求邊的個(gè)數(shù),不妨轉(zhuǎn)換成距離
根據(jù)虛樹建樹得到啟發(fā),如果目前只有兩個(gè)節(jié)點(diǎn)的話,那么該題對(duì)應(yīng)的答案顯然是兩點(diǎn)之間的距離,對(duì)應(yīng)為下圖:
如果此時(shí)再加入一個(gè)點(diǎn) 2,滿足 dfn[ 1 ] < dfn[ 2 ] < dfn[ 3 ],那么對(duì)于點(diǎn) 2 來說無非只有兩種情況:
不位于點(diǎn) 1 到點(diǎn) 3 的路徑上 位于點(diǎn) 1 到點(diǎn) 3 的路徑上
分別對(duì)應(yīng)著下圖的兩種情況:
對(duì)于情況二來說,在本題中對(duì)答案顯然不做貢獻(xiàn),而對(duì)于情況一來說,多出來的一段貢獻(xiàn)已經(jīng)用紅圈標(biāo)記出來了
總結(jié)一下求這個(gè)紅圈中的貢獻(xiàn),實(shí)質(zhì)上就是求 dis( 1 , 2 ) + dis( 2?, 3 ) - dis( 1 , 3 )
其中,dis( x , y ) 是從點(diǎn) x 到點(diǎn) y 的距離,這個(gè)借助 LCA 很容易就能實(shí)現(xiàn),這樣本題的做法就呼之欲出了
求出整棵樹的 dfs 序,將每個(gè)顏色的節(jié)點(diǎn)按照 dfs 序維護(hù),對(duì)于每個(gè)節(jié)點(diǎn) x 來說,設(shè) l 為 dfs 序小于 x 的最大節(jié)點(diǎn),r 為 dfs 序大于 x 的最小節(jié)點(diǎn),mmin 為顏色 c[ x ] 中 dfs 序最小的節(jié)點(diǎn),mmax 為顏色 c[ x ] 中 dfs 序最大的節(jié)點(diǎn),加入點(diǎn) x 分為兩種情況討論:
如果 l 和 r 都存在:ans += dis( x , l ) + dis( x , r ) - dis( l , r ),對(duì)應(yīng)著上圖中的情況 否則:ans += dis( x , mmin ) + dis( x , mmax ) - dis( mmin , mmax ),對(duì)應(yīng)著新加入的點(diǎn)的 dfn 為最小值或最大值的情況,此時(shí)該點(diǎn)對(duì)本題的答案一定具有貢獻(xiàn),隨便找兩個(gè)點(diǎn)配合更新一下答案即可
刪除的話倒著減去貢獻(xiàn)就好了
代碼: ?
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;int limit,deep[N],dp[N][20],id[N],c[N],ans[N],cnt;vector<int>node[N];struct Scmp
{bool operator()(int x,int y){return id[x]<id[y];}
};set<int,Scmp>st[N];void dfs(int u,int fa,int dep)
{id[u]=++cnt;deep[u]=dep;dp[u][0]=fa;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){if(v==fa)continue;dfs(v,u,dep+1);}
}int LCA(int x,int y)
{if(deep[x]<deep[y])swap(x,y);for(int i=limit;i>=0;i--)if(deep[x]-deep[y]>=(1<<i))x=dp[x][i];if(x==y)return x;for(int i=limit;i>=0;i--)if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}return dp[x][0];
}int dis(int x,int y)
{return deep[x]+deep[y]-2*deep[LCA(x,y)];
}void update(int x,int flag)
{if(flag==-1)st[c[x]].erase(x);auto it=st[c[x]].lower_bound(x);if(st[c[x]].empty())ans[c[x]]=0;else if(it==st[c[x]].begin()||it==st[c[x]].end()){ans[c[x]]+=flag*dis(*st[c[x]].begin(),x);ans[c[x]]+=flag*dis(*st[c[x]].rbegin(),x);ans[c[x]]-=flag*dis(*st[c[x]].begin(),*st[c[x]].rbegin());}else{auto nt=it,pre=--it;ans[c[x]]+=flag*dis(*nt,x);ans[c[x]]+=flag*dis(*pre,x);ans[c[x]]-=flag*dis(*pre,*nt);}if(flag==1)st[c[x]].insert(x);
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int n;scanf("%d",&n);limit=log2(n)+1;for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}dfs(1,0,0);for(int i=1;i<=n;i++){scanf("%d",c+i);update(i,1);}int m;scanf("%d",&m);while(m--){char op[5];scanf("%s",op);if(op[0]=='Q'){int x;scanf("%d",&x);printf("%d\n",st[x].empty()?-1:ans[x]/2);}else{int x,y;scanf("%d%d",&x,&y);update(x,-1);c[x]=y;update(x,1);}}return 0;
}
?
總結(jié)
以上是生活随笔 為你收集整理的牛客 - Colorful Tree(dfs序+LCA) 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。