UOJ#192. 【UR #14】最强跳蚤

3 篇文章 0 订阅
1 篇文章 0 订阅
题目大意:给定一颗树,每条边有一个权值,求有多少有序点对使得这两点间路径权值乘起来是完全平方数

我们可以给每个素数随机一个权值,然后把每条边的权值分解质因数,把每个素数换成对应的权值然后异或起来,这样权值乘起来是完全平方数就变成了路径异或和等于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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值