CF1467E Distinctive Roots in a Tree
阿新 • • 發佈:2021-01-12
突然發現深究一些樹上問題還是挺有意思的哈。
顯然對於同一種權值的任意兩個結點,其兩端的部分都是不合法的。
維護兩個標記表示子樹內均不合法和子樹外均不合法即可。但相同權值對數是 \(O\left(n^2\right)\) 的,我們要優化這個過程。
發現很多對都是無用的。\(\texttt{DFS}\) 下去,遇到一個 \(x\) 權值的結點 \(u\),其實只需要與上一個遇到的 \(x\) 權值的結點 \(v\) 做一下就好了,原因如下:
- \(v\) 是 \(u\) 的祖先。\(u\) 與 \(v\) 子樹外的結點做沒有影響。
- \(v\) 不是 \(u\) 的祖先。能不經過其它 \(x\) 權值的除 \(v\)
紅色結點表示權值 \(x\) 的結點,藍色結點就是 \(u\)。
畫圖就會發現這個結論太容易得出啦。
最後再來一次 \(\texttt{DFS}\) 處理所有標記。記一下是否存在子樹外均不合法標記和當前子樹內合法結點數。當一個結點的兩個兒子中都出現子樹外均不合法時答案一定為 \(0\)。細節仔細思考一下。
時間複雜度 \(O\left(n\log n\right)\)。瓶頸在於離散化,放一下程式碼叭。
code:
#include<bits/stdc++.h> using namespace std; #define N 200005 #define For(i,x,y)for(i=x;i<=(y);i++) struct node { int next,to; }e[400005]; map<int,int>col; bool vis[N],fa[N],son[N]; int a[N],head[N],sta[N],last[N],pos[N],g,cnt,top; int read() { int A; bool K; char C; C=A=K=0; while(C<'0'||C>'9')K|=C=='-',C=getchar(); while(C>'/'&&C<':')A=(A<<3)+(A<<1)+(C^48),C=getchar(); return(K?-A:A); } inline void add(int u,int v) { e[++g].to=v; e[g].next=head[u]; head[u]=g; } void dfs(int u,int t) { int i,v,tmp; vis[u]=1; sta[++top]=u; pos[u]=top; if(!col.count(a[u]))tmp=col[a[u]]=++cnt; else { tmp=col[a[u]]; if(vis[last[tmp]])fa[sta[pos[last[tmp]]+1]]=1; else son[last[tmp]]=1; son[u]=1; } last[tmp]=u; for(i=head[u];i;i=e[i].next) { v=e[i].to; if(v==t)continue; dfs(v,u); last[tmp]=u; } vis[u]=0; top--; } pair<int,int>calc(int u,int t) { int i,v,tot,siz; pair<int,bool>pa; tot=siz=0; for(i=head[u];i;i=e[i].next) { v=e[i].to; if(v==t)continue; pa=calc(v,u); /*cout<<v<<' '<<pa.first<<' '<<pa.second<<endl;*/ siz+=pa.second; if(siz>1)cout<<0,exit(0); if(!siz)tot+=pa.first; else if(pa.second)tot=pa.first; } return make_pair((son[u]?0:tot+(!siz)),siz||fa[u]); } int main() { int n,i,u,v; n=read(); For(i,1,n)a[i]=read(); For(i,1,n-1) { u=read(),v=read(); add(u,v),add(v,u); } dfs(1,0); /*For(i,1,n)cout<<son[i]<<' '<<fa[i]<<endl;*/ cout<<calc(1,0).first; return 0; }