题目大意:给定一颗树,每条边有一个权值,求有多少有序点对使得这两点间路径权值乘起来是完全平方数
我们可以给每个素数随机一个权值,然后把每条边的权值分解质因数,把每个素数换成对应的权值然后异或起来,这样权值乘起来是完全平方数就变成了路径异或和等于0
然后路径异或和等于0等价于到根的路径异或和相等,这样就可以直接求出来了...
为了保证准确率,最好在longlong范围内随机权值
明明感觉时间复杂度没什么问题,然而过不了hack数据,....
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cstdlib>
#define N 200010
using namespace std;
bool he[10010];
long long p[10010],cnt;
void getp()
{
long long i,j;
for(i=2;i<=10000;i++)
{
if(!he[i]) cnt++,p[cnt]=i;
for(j=1;j<=cnt&&i*p[j]<=10000;j++)
{
he[i*p[j]]=true;
if(i%p[j]==0) break;
}
}
}
map<long long,long long>P;
long long to[N<<1],nxt[N<<1],pre[N],w[N<<1],cntt;
void ae(long long ff,long long tt,long long ww)
{
cntt++;
to[cntt]=tt;
nxt[cntt]=pre[ff];
pre[ff]=cntt;
w[cntt]=ww;
}
long long a[N],fa[N];
void build(long long x)
{
long long i,j;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(j==fa[x]) continue;
fa[j]=x;
a[j]=a[x]^w[i];
build(j);
}
}
long long ran()
{
return ((long long )rand()<<30)+rand();
}
int main()
{
srand(859985722);
long long n;
scanf("%lld",&n);
long long i,j,x,y,z,t;
getp();
for(i=1;i<=cnt;i++)
P[p[i]]=ran();
for(i=1;i<n;i++)
{
scanf("%lld%lld%lld",&x,&y,&z);
t=0;
for(j=1;j<=cnt&&p[j]*p[j]<=z;j++)
while(z%p[j]==0)
{
z/=p[j];
t^=P[p[j]];
}
if(z!=1)
{
if(!P[z]) P[z]=ran();
t^=P[z];
}
ae(x,y,t);ae(y,x,t);
}
build(1);
sort(a+1,a+n+1);
long long ans=0;
for(i=1;i<=n;i=j)
{
j=i;
while(a[j]==a[i]&&j<=n) j++;
ans+=(long long)(j-i)*(j-i-1);
}
printf("%lld",ans);
}