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

 

 
posted on 2017-12-04 20:31  nhc2014  阅读(132)  评论(0编辑  收藏  举报