[bzoj2152]聰聰可可——點分治

bzoj 2152 聰聰可可

點分治

題意

給定一棵帶權樹,需要統計路徑長度爲3的倍數的路徑條數。

思路

重學點分治。
主要操作就是找重心,那個getrt函數。mx[x]表示x最大的兒子的size,注意這個最大的兒子包括他父親。所以最後那句mx[x]=max(mx[x], n-sz[x]);就是統計他父親那邊的size。

getdis和calc函數都是暴力計算,getdis(x,f)是統計x的子樹中的dis情況。
那個work裏面有個地方要注意,最開始ans+=calc(x, 0);,是計算x爲根的子樹中的對數。但是這樣計算會有重複,所以後面減去ans-=calc(e[i].to, e[i].w);,減去將e[i].to當成深度w的個數。

在計數的時候,將邊權模3,統計在模3意義下的深度,設tim[x]表示深度爲x的點的個數,那麼答案爲tim[1] * tim[2] * 2 + tim[0] * tim[0]。

代碼

//#include<bits\stdc++.h>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int MAXN=20005;
int gcd(int a, int b)
{
    return b==0 ? a : gcd(b, a%b);
}
struct Edge
{
    int to, ne;
    int w;
}e[MAXN*2+7];
int edgenum, head[MAXN];
void addedge(int from, int to, int w)
{
    e[edgenum].to=to, e[edgenum].w=w, e[edgenum].ne=head[from], head[from]=edgenum++;
    e[edgenum].to=from, e[edgenum].w=w, e[edgenum].ne=head[to], head[to]=edgenum++;
}
int sz[MAXN], mx[MAXN], root, vis[MAXN];
void getrt(int x, int f, int n)//找x的子樹內的重心root
{
    sz[x]=1;mx[x]=0;
    for(int i=head[x];~i;i=e[i].ne)
    {
        int to=e[i].to;
        if(to==f||vis[to]) continue;
        getrt(to, x, n);
        sz[x]+=sz[to];
        mx[x]=max(mx[x], sz[to]);
    }
    mx[x]=max(mx[x], n-sz[x]);
    if(mx[x]<mx[root]) root=x;
}
int tim[3];int dis[MAXN];
void getdis(int x, int f)
{
    tim[dis[x]]++;
    for(int i=head[x];~i;i=e[i].ne)
    {
        int to=e[i].to;
        if(to==f||vis[to]) continue;
        dis[to]=(dis[x]+e[i].w)%3;
        getdis(to, x);
    }
}
int calc(int x, int d)
{
    tim[0]=tim[1]=tim[2]=0;
    dis[x]=d;
    getdis(x, -1);
    return tim[0]*tim[0]+tim[1]*tim[2]*2;
}
int ans;
void work(int x)
{
    vis[x]=1;
    ans+=calc(x, 0);
    for(int i=head[x];~i;i=e[i].ne)
    {
        if(vis[e[i].to]) continue;
        ans-=calc(e[i].to, e[i].w);
        root=0;
        getrt(e[i].to, -1, sz[e[i].to]);
        work(root);
    }
}

int main()
{
    int n;
    while(scanf("%d", &n)==1)
    {
        memset(head, -1, sizeof(head));
        for(int i=2;i<=n;i++)
        {
            int a, b, c;scanf("%d%d%d", &a, &b, &c);
            addedge(a, b, c%3);
        }
        tim[0]=tim[1]=tim[2]=0;
        mx[0]=0x3f3f3f3f;ans=0;root=0;
        getrt(1, -1, n);
        work(root);

        int d=gcd(ans, n*n);

        printf("%d/%d\n", ans/d, n*n/d);
    }
    return 0;
}
發佈了169 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章