[BZOJ2152]聪聪可可

link

也许本身想去写点分治的,但是最后因为码量太大就放弃了

然后就顺手写了个树形$dp$,其实跟点分治的思想一样,我们只要每次统计一条路径的上端点,什么意思呢,就是我们将要统计$(u,v)$是否合法呢,只要去$lca(u,v)$就行。然后就定$dp$数组为$dp(i,j)$表示为当前到第i个节点,并且现在长度对$3$取模为的方案数,然后就可以随便$dp$了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=20001;
int gcd(int x,int y){
    if(y==0) return x;
    return gcd(y,x%y);
}
struct node{
    int u,v,w,nex;
}x[MAXN<<1];
int dp[MAXN][4],n,head[MAXN],cnt;//dp(i,j)表示在第i颗子树上选择距离为j%3的方案数 
void add(int u,int v,int w){
    x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++;
}
int ans;
void dfs(int f,int fath){
    dp[f][0]=1;
    for(int ii=head[f];ii!=-1;ii=x[ii].nex){
        if(x[ii].v==fath) continue;
        dfs(x[ii].v,f);
        for(int i=0;i<3;i++){
            int w=x[ii].w%3;
            ans+=dp[x[ii].v][i]*dp[f][(3-((i+w)%3))%3]*2;
        }
        for(int i=0;i<3;i++) dp[f][(i+x[ii].w)%3]+=dp[x[ii].v][i];
    }
    return;
}
int main(){
    memset(head,-1,sizeof(head));
    n=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    dfs(1,0);
    ans+=n;
    int fz=ans,fm=n*n;
    int k=gcd(fz,fm);
    fz/=k,fm/=k;
    printf("%d/%d",fz,fm);
}
View Code
原文地址:https://www.cnblogs.com/si-rui-yang/p/10058292.html