luoguP2634聪聪可可(点分治)
题目链接:https://www.luogu.org/problem/P2634
题意:给一颗树,求距离的模3为0的点对数量,注意(1,1)算一个点对,(1,2)和(2,1)算两个点对。
思路:和点分治模板题很像,那个是求距离为k的点对数,这题更简单一点,求距离模3为0的点对数。还是一般的套路,求重心,计算点到重心的距离模3的值dis[i],然后用num[i]统计dis=i的点数,然后加上所有组合,即(num[0]-1)*num[0]/2+num[1]*num[2],然后减去在同一个子树中的不合法组合。最后因为我们求的是不同点对数,需要乘2,再加上n(自己和自己组成的点对)。
AC代码:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=20005; const int inf=0x3f3f3f3f; struct node1{ int v,w,nex; }edge[maxn<<1]; int n,ans,cnt,head[maxn],sz[maxn],mson[maxn],Min,root,size; int vis[maxn],t,dis[maxn],num[4]; void adde(int u,int v,int w){ edge[++cnt].v=v; edge[cnt].w=w; edge[cnt].nex=head[u]; head[u]=cnt; } int gcd(int a,int b){ return b?gcd(b,a%b):a; } void getroot(int u,int fa){ sz[u]=1,mson[u]=0; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]||v==fa) continue; getroot(v,u); sz[u]+=sz[v]; if(sz[v]>mson[u]) mson[u]=sz[v]; } if(size-sz[u]>mson[u]) mson[u]=size-sz[u]; if(mson[u]<Min) Min=mson[u],root=u; } void getdis(int u,int fa,int len){ dis[++t]=len; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]||v==fa) continue; getdis(v,u,(len+edge[i].w)%3); } } void solve(int x,int y,int f){ t=0; getdis(x,0,y); num[0]=num[1]=num[2]=0; for(int i=1;i<=t;++i) ++num[dis[i]]; ans+=(num[0]-1)*num[0]/2*f; ans+=num[1]*num[2]*f; } void fenzhi(int u,int ssize){ vis[u]=1; solve(u,0,1); for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]) continue; solve(v,edge[i].w%3,-1); Min=inf,root=0; size=sz[v]<sz[u]?sz[v]:(ssize-sz[u]); getroot(v,0); fenzhi(root,size); } } int main(){ scanf("%d",&n); for(int i=1;i<n;++i){ int u,v,w; scanf("%d%d%d",&u,&v,&w); adde(u,v,w%3); adde(v,u,w%3); } Min=inf,root=0,size=n; getroot(1,0); fenzhi(root,n); ans=2*ans+n; int tmp1=gcd(ans,n*n); ans/=tmp1; int tmp2=n*n/tmp1; printf("%d/%d\n",ans,tmp2); return 0; }
朋友们,无论这个世界变得怎样,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。