BZOJ2152 聪明可可 点分治

题意传送门

思路:基本的点分治思路,num数组记录从u点开始路径长度分别为1或者2或者3的路径长度(取模3意义下),然后做一个简单的容斥就好了。

  为了避免计数的麻烦,<u,u>这样的点单独计算,也就是最后的答案加上n就可以了。

  bzoj不支持c14,我的代码是在洛谷交的。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dep(i,b,a) for(int i=b;i>=a;--i)
#define clr(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pii pair<int,int >
using namespace std;
typedef long long ll;
ll rd()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=20010;
const int inf=0x3f3f3f3f;
int T,n,m;
struct edge{
    int to,w;
};
vector<edge >ve[maxn];
ll ans=0;
int rt,mx,dis[maxn];
ll num[3];
int siz[maxn],size;
bool vis[maxn];
int u,v,w;
void getrt(int u,int p){
    siz[u]=1;
    int tmp=0;
    for(auto &st:ve[u]){
        int v=st.to;
        if(vis[v]||v==p)continue;
        getrt(v,u);
        siz[u]+=siz[v];
        tmp=max(tmp,siz[v]);
    }
    tmp=max(size-siz[u],tmp);
    if(tmp<mx){
        mx=tmp;
        rt=u;
    }
}
void getdis(int u,int p){
    dis[u]%=3;
    num[dis[u]]++;
    for(auto &st:ve[u]){
        int v=st.to,w=st.w;
        if(v==p||vis[v])continue;
        dis[v]=(dis[u]+w)%3;
        getdis(v,u);
    }
}
ll cal(int u,int val){
    dis[u]=val;
    clr(num,0);
    getdis(u,0);
    ll sum=2*num[1]*num[2]+2*(num[0]-1)+(num[0]-1)*(num[0]-1-1);
    return sum;
}
void dfs(int u){
    vis[u]=1;
    ans+=cal(u,0);
    for(auto &st:ve[u]){
        int v=st.to,w=st.w;
        if(vis[v])continue;
        ans-=cal(v,w);
        size=siz[v];
        mx=inf;
        getrt(v,0);
        dfs(rt);
    }
}
int main(){
    cin>>n;
    rep(i,1,n-1){
        u=rd(),v=rd(),w=rd();
        w=w%3;
        ve[u].pb({v,w});
        ve[v].pb({u,w});
    }
    size=n;
    mx=inf;
    getrt(1,0);
    dfs(1);
    ans+=n;
    ll an2=1ll*n*n;
    ll gc=__gcd(an2,ans);
    printf("%lld/%lld\n",ans/gc,an2/gc);
}

 

posted @ 2019-08-25 12:34  光芒万丈小太阳  阅读(211)  评论(0编辑  收藏  举报