zoukankan      html  css  js  c++  java
  • 【uoj#192】[UR #14]最强跳蚤 Hash

    题目描述

    给定一棵 $n$ 个点的树,边有边权。求简单路径上的边的乘积为完全平方数的点对 $(x,y) , x e y$ 的数目。


    题解

    Hash

    一个数是完全平方数,当且仅当每个质因子出现次数都是偶数。

    因此给每一个质因子赋一个随机权值,一个数的权值等于它所有出现次数为奇数的质因子权值的异或。那么边权乘积的权值就是边权权值的异或。问题转化为求有多少条路径异或值为0。

    显然, $x$ 到 $y$ 异或和为0,等价于 $x$ 到根和 $y$ 到根异或和为0。因此求出一个点到根节点的路径的权值异或和,问题转化为求有多少个相等的数。排序之后统计即可。

    分解质因数可以先筛出 $sqrt z$ 内的质数,只用质数试除,单次的时间复杂度为 $O(frac{sqrt z_i}{ln z_i})$ 。

    时间复杂度 $O(nfrac{sqrt z_i}{ln z_i})$ 。

    注意:由于生日攻击原理,权值的范围需要远大于 $n^2$ ,需要long long级别。UOJ测评环境为Linux,randmax为2147483647。

    #include <map>
    #include <cstdio>
    #include <algorithm>
    #define N 100010
    using namespace std;
    typedef long long ll;
    int prime[10010] , tot , np[10010] , head[N] , to[N << 1] , next[N << 1] , cnt;
    ll val[N << 1] , sum[N];
    map<int , ll> mp;
    void init()
    {
    	int i , j;
    	for(i = 2 ; i <= 10000 ; i ++ )
    	{
    		if(!np[i]) prime[++tot] = i;
    		for(j = 1 ; j <= tot && i * prime[j] <= 10000 ; j ++ )
    		{
    			np[i * prime[j]] = 1;
    			if(i % prime[j] == 0) break;
    		}
    	}
    }
    inline void add(int x , int y , ll z)
    {
    	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x , int fa)
    {
    	int i;
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa)
    			sum[to[i]] = sum[x] ^ val[i] , dfs(to[i] , x);
    }
    int main()
    {
    	init();
    	srand(20011011);
    	int n , i , j , x , y , z;
    	ll t , v , ans = 0;
    	scanf("%d" , &n);
    	for(i = 1 ; i < n ; i ++ )
    	{
    		scanf("%d%d%d" , &x , &y , &z) , v = 0;
    		for(j = 1 ; j <= tot ; j ++ )
    		{
    			if(z % prime[j] == 0)
    			{
    				if(mp.find(prime[j]) == mp.end()) mp[prime[j]] = (ll)rand() << 31 | rand();
    				t = mp[prime[j]];
    				while(z % prime[j] == 0) z /= prime[j] , v ^= t;
    			}
    		}
    		if(z != 1)
    		{
    			if(mp.find(z) == mp.end()) mp[z] = (ll)rand() << 31 | rand();
    			v ^= mp[z];
    		}
    		add(x , y , v) , add(y , x , v);
    	}
    	dfs(1 , 0);
    	sort(sum + 1 , sum + n + 1);
    	for(i = j = 1 ; i <= n ; i = j)
    	{
    		while(j <= n && sum[i] == sum[j]) j ++ ;
    		ans += (ll)(j - i) * (j - i - 1);
    	}
    	printf("%lld" , ans);
    	return 0;
    }
    
  • 相关阅读:
    实现非父子之间通信,兄弟组件之间的数据传递--eventBus
    Vue的核心思想是什么..........
    Vue——核心思想--mvvm
    高效遍历匹配Json数据,避免嵌套循环
    js 报错(intermediate value)(...) is not a function
    【Three.js】three.js 中的矩阵变换及两种旋转表达方式
    【Vue源码】将二维数组变为一维数组
    【移动端】cordova实现退出app功能
    【移动端】ionic cordova 打包debug调试版、release发布版
    【Vue源码】document.querySelector()方法
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8618861.html
Copyright © 2011-2022 走看看