背景:
h
e
h
e
.
.
.
hehe...
hehe...
题目传送门:
https://www.luogu.org/problemnew/show/P2634
题意:
在树上选两个点权值和是
3
3
3的倍数的概率。
思路:
点分治模板。
这一就不用两重循环暴力了,因为
m
o
d
  
3
=
0
\mod 3=0
mod3=0就两种情况:
0
+
0
,
1
+
2
0+0,1+2
0+0,1+2(取模后)。统计答案即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,len=0,ans=0;
int last[200010],size[200010],msize[200010],dis[200010];
bool bz[200010];
struct node{int x,y,z,next;} a[200010];
int SIZE,MIN,ROOT,tot;
void ins(int x,int y,int z)
{
a[++len]=(node){x,y,z,last[x]}; last[x]=len;
}
void find_root(int x,int fa)
{
size[x]=1;
msize[x]=0;
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa||bz[y]) continue;
find_root(y,x);
size[x]+=size[y];
msize[x]=max(msize[x],size[y]);
}
msize[x]=max(msize[x],SIZE-size[x]);
if(MIN>msize[x]) MIN=msize[x],ROOT=x;
}
void get_dis(int x,int fa,int z)
{
dis[++tot]=z;
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(bz[y]||y==fa) continue;
get_dis(y,x,z+a[i].z);
}
}
int sum[10];
void solve(int x,int z,int op)
{
tot=0;
get_dis(x,0,z);
sum[0]=sum[1]=sum[2]=0;
for(int i=1;i<=tot;i++)
sum[dis[i]%3]++;
ans+=(sum[0]*sum[0]+2*sum[1]*sum[2])*op;
}
void dfs(int x)
{
bz[x]=true;
solve(x,0,1);
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(bz[y]) continue;
solve(y,a[i].z,-1);
SIZE=size[y],MIN=SIZE,ROOT=0;
find_root(y,0);
dfs(ROOT);
}
}
int gcd(int x,int y)
{
return !y?x:gcd(y,x%y);
}
int main()
{
int x,y,z;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&x,&y,&z);
ins(x,y,z),ins(y,x,z);
}
SIZE=n,MIN=n,ROOT=0;
find_root(1,0);
dfs(ROOT);
int GCD=gcd(ans,n*n);
printf("%d/%d",ans/GCD,n*n/GCD);
}