题解 P2634 【聪聪可可】
其实树状DP也可以做的,而且复杂度比点分治低......
我们定义 f[x][0,1,2] 表示在点x及其子树中,距离点x的距离在模3意义下为0,1,2的点分别有多少个。
转移的话直接从子树加上边长转移。
统计答案时需要在转移某一个子树前进行统计。其实就是一个卷积(雾?说人话!),说白了就是让 前面的子树的点到点x的距离 与 当前子树中的点到点x的距离 在模3意义下为0。因为点对有序,所以乘2。
初值为f[x][0]=1。最后答案加上 每个点到本身的方案书(其实就是n) , 去和n*n取gcd即可。
最后上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define debug cout
using namespace std;
const int maxn=2e4+1e2;
int s[maxn],t[maxn<<1],nxt[maxn<<1],l[maxn<<1];
int f[maxn][3];
int n,ans,full,g;
inline void addedge(int from,int to,int len)
{
static int cnt = 0;
t[++cnt] = to;
l[cnt] = len;
nxt[cnt] = s[from];
s[from] = cnt;
}
inline int mod(int x)
{
return (x%3+3)%3;
}
inline int gcd(int x,int y)
{
if( ! ( x && y ) )
return x | y;
register int t;
while( t = x % y )
x = y , y = t;
return y;
}
inline void dfs(int pos,int fa)
{
f[pos][0] = 1;
for(int at=s[pos];at;at=nxt[at])
{
if( t[at] == fa )
continue;
dfs(t[at],pos);
for(int i=0;i<3;i++)
ans += f[t[at]][i] * f[pos][mod(-i-l[at])] * 2;
for(int i=0;i<3;i++)
f[pos][mod(i+l[at])] += f[t[at]][i];
}
}
inline int getint()
{
int ret = 0 , fix = 1;
char ch = getchar();
while( ! isdigit(ch) )
fix = ch == '-' ? -1 : fix,
ch = getchar();
while( isdigit(ch) )
ret = ret * 10 + ( ch - '0' ),
ch = getchar();
return ret * fix;
}
int main()
{
n = getint();
for(int i=1,a,b,l;i<n;i++)
{
a = getint() , b = getint() , l = getint();
addedge(a,b,l);
addedge(b,a,l);
}
dfs(1,-1);
ans += n;
full = n * n;
g = gcd( ans , full );
ans /= g , full /= g;
printf("%d/%d\n",ans,full);
return 0;
}