[国家集训队][bzoj 2152] 聪聪可可 [点分治]

题面:

http://www.lydsy.com/JudgeOnline/problem.php?id=2152

思路:

题目要求统计书上路径信息,想到树上分治算法

实际上这是一道点分治裸题,我就不瞎BB思路了,直接上做法。

对于一个节点,设dis[i]为其他节点到这个节点的距离 MOD 3

那么可以证明,一条经过该节点的合法路径(i,j),( dis[i] + dis[j] ) % 3==0

因此对每个节点求出其子树内的dis,经过该点的路径数即为(dis[i]==1的点数)*(dis[i]==2的点数)*2 + (dis[i]==0的点数)^2

递归求解

递归进入一个节点u时,ans先加入calc(u,0),即为上述求解过程(dis[u]==0开始)。

每一次对于当前递归到的节点的每一棵子树,ans先减去dis(v,e[i].w),即为从(dis[v]==e[i].w)开始,为的是去掉重复计算的部分。

求这个子树的重心

方法:

递归进入子树中每一个节点,统计其子最大的子树大小(包括连向其父节点的那一棵)

这个最大值最小的节点就是树的重心。

如果每次从这里进入,那么进入的子树的size一定小于整棵当前树的size的一半

这样递归下去最多log n层

求完这棵子树的重心以后从这个重心递归进入,求该子树的解

最后ans即为答案数目(ans初始为0)

 

Code:

 1 // luogu-judger-enable-o2
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 void _swap(int &x,int &y){x^=y;y^=x;x^=y;}
 8 int _max(int x,int y){return (x>y)?x:y;}
 9 inline int read(){
10     int re=0,flag=1;char ch=getchar();
11     while(ch>'9'||ch<'0'){
12         if(ch=='-') flag=-1;
13         ch=getchar();
14     }
15     while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
16     return re*flag;
17 }
18 int n,cnt,ans,dis[20010],first[20010],tmp[5],root,siz[20010],son[20010],sum;
19 bool vis[20010];
20 struct edge{
21     int to,next,w;
22 }a[40010];
23 inline void add(int u,int v,int w){
24     a[++cnt]=(edge){v,first[u],w};first[u]=cnt;
25     a[++cnt]=(edge){u,first[v],w};first[v]=cnt;
26 }
27 int gcd(int x,int y){return (y?gcd(y,x%y):x);};
28 void getroot(int u,int f){
29     //cout<<"getroot "<<u<<" "<<f<<"\n";
30     int i,v;
31     siz[u]=1;son[u]=0;
32     for(i=first[u];~i;i=a[i].next){
33         v=a[i].to;
34         if(v==f||vis[v]) continue;
35         getroot(v,u);
36         siz[u]+=siz[v];
37         son[u]=_max(son[u],siz[v]);
38     }
39     son[u]=_max(son[u],sum-siz[u]);//统计父亲节点的那棵子树,sum为当前的整棵子树的size
40     //cout<<"finish getroot "<<son[u]<<'\n';
41     if(son[u]<son[root]) root=u;
42 }
43 void gettmp(int u,int f){
44     //cout<<"gettmp "<<u<<" "<<f<<"\n";
45     int i,v;
46     tmp[dis[u]]++;
47     for(i=first[u];~i;i=a[i].next){
48         v=a[i].to;
49         if(v==f||vis[v]) continue;
50         //cout<<" to "<<v<<'\n';
51         dis[v]=(dis[u]+a[i].w)%3;
52         gettmp(v,u);
53     }
54 }
55 int calc(int u,int d){//即为文中描述的calc
56     dis[u]=d%3;tmp[0]=tmp[1]=tmp[2]=0;
57     gettmp(u,0);
58     return tmp[1]*tmp[2]*2+tmp[0]*tmp[0];
59 }
60 void dfs(int u){
61     //cout<<"dfs "<<u<<"\n";
62     int i,v;
63     vis[u]=1;ans+=calc(u,0);
64     for(i=first[u];~i;i=a[i].next){
65         v=a[i].to;
66         if(vis[v]) continue;
67         ans-=calc(v,a[i].w);
68         sum=siz[v];root=0;//更新root和sum
69         getroot(v,u);
70         dfs(root);
71     }
72 }
73 int main(){
74     // freopen("cckk.in","r",stdin);
75     // freopen("cckk.out","w",stdout);
76     memset(first,-1,sizeof(first));
77     int i,t1,t2,t3;
78     n=read();
79     for(i=1;i<n;i++){
80         t1=read();t2=read();t3=read();
81         add(t1,t2,t3);
82     }
83     //cout<<"finish read in\n";
84     sum=n;son[0]=n;//将son[0]初始化为极大值
85     getroot(1,0);
86     dfs(1);
87     int div=gcd(n*n,ans);//注意约分
88     printf("%d/%d",ans/div,n*n/div);
89 }

 

posted @ 2018-01-31 14:09  dedicatus545  阅读(372)  评论(0编辑  收藏  举报