2019沈阳网路赛 D. Fish eating fruit (点分治)

传送门

题意

给一颗树,统计出树上 \(\%3=0,1,2\) 的路径总和分别是多少

题解

不知道为什么点分治的题 \(n\) 一般都给 \(10^4\)
这个题用点分治是很好做的,统计对三取余分别为 \(0,1,2\) 的路径的条数和总长,然后累加答案就行

代码

//尝试淀粉质
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=1e4+10;
const int mod=1e9+7;
int n,head[MAXN],to[MAXN*2],nxt[MAXN*2],val[MAXN*2],tot;
int siz[MAXN],maxp[MAXN],sum,rt,dead[MAXN],cnt,dis[MAXN];
LL ans[3],dissum[3],disnum[3];

void add(int u,int v,int w){
    to[++tot]=v;val[tot]=w;nxt[tot]=head[u];head[u]=tot;
}

void getrt(int u,int fa){
    siz[u]=1;maxp[u]=0;
    for(int i=head[u];i;i=nxt[i]){
        if(to[i]==fa||dead[to[i]]) continue;
        getrt(to[i],u);
        siz[u]+=siz[to[i]];
        maxp[u]=max(maxp[u],siz[to[i]]);
    }
    maxp[u]=max(maxp[u],sum-siz[u]);
    if(maxp[u]<maxp[rt]) rt=u;
}

void getdis(int u,int fa,int w){
    dis[++cnt]=w;
    for(int i=head[u];i;i=nxt[i]){
        if(to[i]==fa||dead[to[i]]) continue;
        getdis(to[i],u,w+val[i]);
    }
}

void calc(int u){
    disnum[0]=1;
    for(int i=head[u];i;i=nxt[i]){
        if(dead[to[i]]) continue;
        cnt=0;
        getdis(to[i],u,val[i]);
        for(int j=1;j<=cnt;j++){
            for(int k=0;k<3;k++)
                (ans[(dis[j]%3+k)%3]+=dissum[k]+disnum[k]*dis[j]%mod)%=mod;
        }
        for(int j=1;j<=cnt;j++){
            (dissum[dis[j]%3]+=dis[j])%=mod;
            disnum[dis[j]%3]++;
        }
    }
    memset(dissum,0,sizeof(dissum));
    memset(disnum,0,sizeof(disnum));
}

void divide(int u){
    dead[u]=1;
    calc(u);
    for(int i=head[u];i;i=nxt[i]){
        if(dead[to[i]]) continue;
        maxp[rt=0]=sum=siz[to[i]];
        getrt(to[i],0);
        getrt(rt,0);
        divide(rt);
    }
}

void solve(){
    tot=0;
    memset(head,0,sizeof(head));
    memset(dead,0,sizeof(dead));
    memset(ans,0,sizeof(ans));
    for(int i=1,u,v,w;i<n;i++){
        scanf("%d%d%d",&u,&v,&w);
        u++,v++;
        add(u,v,w);add(v,u,w);
    }
    maxp[rt=0]=sum=n;
    getrt(1,0);
    getrt(rt,0);
    divide(rt);
    for(int i=0;i<3;i++) ans[i]=ans[i]*2%mod;
    printf("%lld %lld %lld\n",ans[0],ans[1],ans[2]);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
#endif
    while(~scanf("%d",&n)) solve();
    return 0;
}

其实用这种思想,写树形dp也不难了

猜你喜欢

转载自www.cnblogs.com/BakaCirno/p/12307461.html