1. 程式人生 > 實用技巧 >JZOJ 1967.【2011集訓隊出題】聰聰可可

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);
}