[国家集训队][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 }