JZOJ 1967.【2011集训队出题】聪聪可可

题目

【2011集训队出题】聪聪可可

思路

看看做做 阴阳 这道题
极力推荐
自从做了这道题后,这些题就变成秒切的题了

很容易想到求节点到分治中心的距离,然后 \(\bmod 3\)
那么在求根节点一棵子树的答案时直接加上 \(dis[(3-x) mod 3]\) 的个数
用个桶 \(buc\) 来记录,若当前节点的 \(dis \bmod 3\) 后结果为 \(0\),说明它到跟也为合法路径,此时 \(res\) 要额外 \(+1\)
统计完一个子树的贡献后再将子树的信息加入桶中
统计完所有子树,重新选根前再 \(dfs\) 一遍清除 \(buc\)

\(Code\)

#include<cstdio>
#include<iostream>
using namespace std;

const int N = 2e4 + 5;
int n , h[N] , tot , size , siz[N] , son[N] , dis[N] , use[N] , ans , rt , buc[5];

struct edge{
	int to , nxt , w;
}e[N * 2];

inline void add(int x , int y , int z)
{
	e[++tot].to = y;
	e[tot].w = z;
	e[tot].nxt = h[x];
	h[x] = tot;
}

inline void getrt(int x , int fa)
{
	son[x] = 0 , siz[x] = 1;
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa || use[v]) continue;
		getrt(v , x);
		siz[x] += siz[v];
		son[x] = max(son[x] , siz[v]);
	}
	son[x] = max(son[x] , size - siz[x]);
	rt = son[x] < son[rt] ? x : rt;
}

inline void getdis(int x , int fa)
{
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa || use[v]) continue;
		dis[v] = (dis[x] + e[i].w) % 3;
		getdis(v , x);
	}
}

inline int dfs(int x , int fa)
{
	int res = 0;
	res += buc[(3 - dis[x]) % 3] + (dis[x] == 0 ? 1 : 0);
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa || use[v]) continue;
		res += dfs(v , x);
	}
	return res;
}

inline void fill(int x , int fa)
{
	buc[dis[x]]++;
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa || use[v]) continue;
		fill(v , x);
	}
}

inline void clear(int x , int fa)
{
	buc[dis[x]]--;
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa || use[v]) continue;
		clear(v , x);
	}
	dis[x] = 0;
}

inline int calc(int x)
{
	dis[x] = 0;
	getdis(x , 0);
	int res = 0;
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (use[v]) continue;
		res += dfs(v , x) , fill(v , x);
	}
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (use[v]) continue;
		clear(v , x);
	}
	return res;
}

inline void divide(int x)
{
	use[x] = 1 , ans += calc(x);
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (use[v]) continue;
		size = siz[v] , rt = 0;
		getrt(v , x) , divide(rt);
	}
}

inline int gcd(int a , int b){return b == 0 ? a : gcd(b , a % b);}

int main()
{
	scanf("%d" , &n);
	int u , v , w;
	for(register int i = 1; i < n; i++) 
	{
		scanf("%d%d%d" , &u , &v , &w);
		add(u , v , w) , add(v , u , w);
	}
	son[0] = 2e9 , size = n , rt = 0;
	getrt(1 , 0) , divide(rt);
	ans = ans * 2 + n;
	int tmp = n * n , d = gcd(ans , tmp);
	printf("%d/%d" , ans / d , tmp / d);
}
posted @ 2020-07-31 19:55  leiyuanze  阅读(92)  评论(0编辑  收藏  举报