BZOJ2152: 聪聪可可
【传送门:BZOJ2152】
简要题意:
给出一棵n个点的树,和每条边的边权,求出有多少个点对,它们的距离为3的倍数
最后输出求出的点对数/总点对数的最简分数
题解:
点分治例题
求值时设t[i]为路径的值%3==i的情况数,那么答案就为t[1]*t[2]*2+t[0]*t[0]
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; struct node { int x,y,d,next; }a[81000];int len,last[41000]; void ins(int x,int y,int d) { len++; a[len].x=x;a[len].y=y;a[len].d=d; a[len].next=last[x];last[x]=len; } int tot[41000],root,sum,ms[41000]; bool v[41000]; void getroot(int x,int fa) { tot[x]=1;ms[x]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa&&v[y]==false) { getroot(y,x); tot[x]+=tot[y]; ms[x]=max(ms[x],tot[y]); } } ms[x]=max(ms[x],sum-tot[x]); if(ms[root]>ms[x]) root=x; } int t[3]; void ask(int x,int fa,int d) { t[d%3]++; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa&&v[y]==false) { ask(y,x,(d+a[k].d)%3); } } } int ans=0; int k; int cal(int x,int d) { t[0]=t[1]=t[2]=0; ask(x,0,d); return 2*t[1]*t[2]+t[0]*t[0]; } void solve(int x) { ans+=cal(x,0); v[x]=true; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) { ans-=cal(y,a[k].d); sum=tot[y]; ms[0]=1<<31-1; root=0;getroot(y,x); solve(root); } } } int gcd(int a,int b) { if(a==0) return b; else return gcd(b%a,a); } int main() { int n; scanf("%d",&n); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); ins(x,y,d);ins(y,x,d); } memset(v,false,sizeof(v)); ans=0; sum=n; ms[0]=1<<31-1; root=0;getroot(1,0); solve(root); int all=n*n; int GCD=gcd(ans,all); printf("%d/%d\n",ans/GCD,all/GCD); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚