bzoj2152: 聪聪可可

题目链接

bzoj2152: 聪聪可可

题解

其实本来想找点分的题做,然后就用dp水了
当然,也有好好用点分水一遍的QWQ
dp,维护子树中点到子树跟的距离,%3分类的个数,转移很好写吧 :上,
点分:维护当前树中点到根的距离分类,统计答案好些吧,因为会在当前树的某棵子树下出现非最短路径,容斥一下就好了,下
当然,dp复杂度是更优的
dp

#include<cstdio> 
#include<algorithm> 
inline int read() { 
    int x = 0,f = 1;
    char c = getchar(); 
    while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar(); } 
    while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar(); 
    return x * f; 
} 
const int maxn = 40007; 
 
struct node { 
    int v,w,next; 
}edge[maxn << 1]; 
int head[maxn],num = 0; 
inline void add_edge(int u,int v,int w) { 
    edge[++ num].v = v;edge[num].w = w;edge[num].next = head[u]; head[u] = num; 
} 
int n; 
int dp[maxn][3]; 
int ans = 0; 
void dfs(int x,int fa) { 
    dp[x][0] = 1; 
    for(int i = head[x];i ;i = edge[i].next) { 
        int v = edge[i].v; 
        if(v == fa) continue; 
        dfs(v,x); 
        for(int j = 0;j < 3;++ j) { 
            int tmp = (j + edge[i].w) % 3; 
            ans += dp[v][j] * dp[x][(3 - tmp) % 3] * 2; 
        } 
        //printf("%d\n",ans); 
        for(int j = 0;j < 3;++ j) 
            dp[x][(j + edge[i].w) % 3] += dp[v][j]; 
    }   
} 
int gcd(int x,int y) { 
    if(!y) return x;else return gcd(y,x % y); 
} 
int main() { 
    n = read(); 
    for(int u,v,w,i = 1;i < n;++ i)  {
        u = read(),v = read(),w = read(); 
        add_edge(u,v,w),add_edge(v,u,w);    
    } 
    dfs(1,0);   
    ans += n; 
    int g = gcd(ans,n * n); 
    printf("%d/%d\n",ans / g,n * n/g);  
    return 0; 
}  


点分

#include<cstdio> 
#include<algorithm> 
inline int read() { 
    int x = 0,f = 1;
    char c = getchar(); 
    while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar(); } 
    while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar(); 
    return x * f; 
} 
const int maxn = 40007; 
struct node { 
    int v,w,next; 
} edge[maxn << 1]; 
int head[maxn],num = 0,ans = 0; 
bool vis[maxn]; 
inline void add_edge(int u,int v,int w) { 
     edge[++ num].v = v;edge[num].w = w;edge[num].next = head[u]; head[u] = num; 
} 
 
int n,son[maxn],dis[maxn],cnt[maxn],f[maxn],root,tot; 
void get_root(int x,int fa) {
    son[x] = 1;f[x] = 0; 
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v; 
        if(v == fa || vis[v]) continue; 
        get_root(v,x); 
            son[x] += son[v];f[x] = std::max(f[x],son[v]);  
    } 
    f[x] = std::max(f[x],tot - son[x]); 
    if(f[x] < f[root]) root = x;     
} 
void get_dis(int x,int fa) { 
    cnt[dis[x]] ++;
    for(int i = head[x];i;i = edge[i].next) {
        int v = edge[i].v; 
            if(vis[v] || v == fa) continue ;
        dis[v] = (dis[x] + edge[i].w) % 3;      
        get_dis(v,x); 
    } 
} 
int calc(int x,int Dis) { 
    dis[x] = Dis;cnt[0] = cnt[1] = cnt[2] = 0; 
    get_dis(x,0); 
    return cnt[1] * cnt[2] * 2 + cnt[0] * cnt[0]; 
} 
void sol(int x) { 
    ans += calc(x,0),vis[x] = 1; 
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v; 
            if(vis[v]) continue; 
        ans -= calc(v,edge[i].w); 
        root = 0;tot = son[v]; 
        get_root(v,0);sol(root); 
    } 
} 
int gcd(int x,int y) { 
    return !y ? x : gcd(y,x % y); 
} 
int main() { 
    n  = read(); 
    for(int u,v,w,i = 1;i < n;++ i) { 
        u = read(),v = read(),w = read() % 3; 
        add_edge(u,v,w),add_edge(v,u,w); 
    } 
    tot = n;f[0] = n + 1; 
    get_root(1,0); 
    sol(root); 
    int g = gcd(ans,n * n); 
    printf("%d/%d\n",ans / g,n * n / g); 
    return 0; 
}   
posted @ 2018-06-26 19:29  zzzzx  阅读(98)  评论(0编辑  收藏  举报