这道题还是比较显然的,我们用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; }