这道题还是比较显然的,我们用dp[x][y]来表示在x这个点往下取一条路段和sum%3==y的路有几种方案,很明显它可以
由dp[son[x]][(y+3-w)%3]来更新(w为son[x]到x这段路的长度)
,如果用ans[x]来表示以x为根的子树中有多少种方案,我们分两种情况,第一种这段路不经过x点那么很明显,它肯定已被
计算在ans[son[x]]中,所以我们要把ans[x]加上ans[son[x]]
,第二种,经过x点,那么这段路就由不同x的不同儿子上的两条链连接起来,假设当前子树的一个儿子上,有a条路段和(sum%3)==2的,
那么a*dp[x][1]就是连接起来为0的一部分答案,
但是因为dp[x][1]中有一部分链也是在这个儿子上的,所以加上的不是a*dp[x][1],事实上还要去掉在同一个儿子路径的答案。
不过查了一下,这其实是点分治的裸题,不过我不会啊!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define maxn 20009
using namespace std;
vector<int>g1[maxn],g2[maxn];
bool vis[maxn];
vector< pair<int,int> >son[maxn];
int dp[maxn][5],n,ans[maxn];
int getgcd(int x,int y)
{
if (y==0) return x;
return getgcd(y,x%y);
}
void dfs(int x)
{
if (vis[x]) return;
vis[x]=true;
dp[x][0]=1;
//因为可以直接去在本身,所以要赋初值为1
for (int i=0;i<g1[x].size();i++)
if (!vis[g1[x][i]])
{
int to=g1[x][i],w=g2[x][i];
w=w%3;
son[x].push_back(make_pair(to,w));
dfs(to);
ans[x]+=ans[to];
for (int j=0;j<=2;j++) dp[x][(j+w)%3]+=dp[to][j];
}
ans[x]+=dp[x][0];
//因为方案如果同取在一个点就算一个答案,所以要加上。
int t1,t2;
//下面是计算经过X点两条链构成的方案
for (int i=0;i<son[x].size();i++)
{
pair<int,int>to=son[x][i];
for (int j=0;j<=2;j++)
{
t1=(3-(j+to.second)%3)%3;
t2=(t1+3-to.second)%3;
ans[x]+=dp[to.first][j]*(dp[x][t1]-dp[to.first][t2]);
//cout<<x<<" "<<son[i].first<<" "<<t1<<" "<<t2<<endl;
}
}
}
int main()
{
scanf("%d",&n);
int x,y,w;
for (int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&w);
g1[x].push_back(y);g2[x].push_back(w);
g1[y].push_back(x);g2[y].push_back(w);
}
dfs(1);
int ans1=ans[1],ans2=n*n;
int gcd=getgcd(ans2,ans1);
printf("%d/%d",ans1/gcd,ans2/gcd);
return 0;
}