1. 程式人生 > 其它 >P2634 [國家集訓隊]聰聰可可 題解

P2634 [國家集訓隊]聰聰可可 題解

題面

從點分治訓練題點過來的,發現可以用樹形dp……具體做法就是說,設 \(f_{u,0/1/2}\) 表示點 \(u\) 的子樹中(包含點 \(u\)),距離 \(\mod 3\) 值為 \(0/1/2\) 的點數。轉移和求答案都是 \(O(1)\) 的,總複雜度 \(O(n)\)

通過這個題可以體會一下點分治的作用:當這道題的第二維很大的時候,往往無法使用樹形dp,這時候考慮點分治之後再去做一個樹形dp或者two pointers。由於點分治擁有隻花費一個 \(\log\) 就能轉化成一個比較好維護的問題的性質,所以經常在一些樹形dp複雜度不優秀的題目中使用。

點選檢視程式碼
#include<iostream>
#include<cstdio>
#define gt(x) (((x)%3+3)%3)
using namespace std;
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
const int N=2e4+13;
struct Edge{int v,w,nxt;}e[N<<1];
int n,tot,h[N],f[N][3],ans;
inline void add(int u,int v,int w){e[++tot]=(Edge){v,w,h[u]};h[u]=tot;}
void dfs(int u,int fa){
	f[u][0]=1;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa) continue;
		dfs(v,u);int tmp=e[i].w%3;
		ans+=f[u][0]*f[v][gt(3-tmp)]+f[u][1]*f[v][gt(2-tmp)]+f[u][2]*f[v][gt(1-tmp)];
		f[u][0]+=f[v][gt(3-tmp)],f[u][1]+=f[v][gt(1-tmp)],f[u][2]+=f[v][gt(2-tmp)];
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1,u,v,w;i<n;++i) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
	dfs(1,0);ans=(ans*2+n);
	int g=gcd(ans,n*n);
	printf("%d/%d",ans/g,n*n/g);
	return 0;
}