cunzai_zsy0531

关注我

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;
}
posted @ 2022-05-11 18:48  cunzai_zsy0531  阅读(14)  评论(0编辑  收藏  举报