• 【洛谷P7854】GCD Tree


    题目

    题目链接:https://www.luogu.com.cn/problem/P7854
    给你 (n) 个点,编号分别为 (1,2,ldots,n),点权分别为 (a_1,a_2,ldots,a_n)
    请你用这 (n) 个点构造一棵树,使得 (forall 1 le i < j le n)(gcd(a_i, a_j) = a_{operatorname{lca}(i, j)})
    若无解,报告之,否则输出树的形态。

    思路

    首先可以把序列排序去重,相同点权的点全部连向其中一个就行了。下面所有点的编号都是点权。
    对于一个点 (x),显然其子树内所有点都是 (x) 的倍数。同时所有 (x) 倍数的点都必须在 (x) 的子树内。
    那么对于 (x) 的倍数 (y),如果 (y) 没有父节点,则把 (y) 的父节点设为 (z);如果 (y) 已经有父节点 (z) 了,且 (z) 不是 (x) 的倍数,那么显然无解;如果 (y) 的父节点 (z)(x) 的倍数,那么只需要考虑 (z)(x) 之间的连边,可以不用改 (y) 的父亲。
    从大到小枚举 (x) 跑一遍就好了。
    但是这样搞出来的数只能保证 (a_{ ext{lca}(i,j)}|gcd(a_i,a_j))。并不一定合法。也就是说,我们只需要把不合法情况判掉就行了。
    下面的思路是我抄 QuantAsk 神仙的 /bx
    如果对于所有 ((i,j)) 都有存在权值为 (gcd(a_i,a_j)) 的点,那么我们的构造是一定合法的。且这个东西是充要的。
    所以可以容斥一下求出每一个数作为 (gcd) 出现的次数,如果任意一个数 (x) 作为 (gcd) 出现过,但是没有权值为 (x) 的点,就无解了。
    时间复杂度 (O((n+V)log V))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1000010;
    int n,m,a[N],b[N],fa[N],pos[N];
    ll cnt[N];
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		b[i]=a[i]; cnt[a[i]]++;
    		if (!pos[a[i]]) pos[a[i]]=i;
    	}
    	for (int i=1;i<=1e6;i++)
    		for (int j=i*2;j<=1e6;j+=i)
    			cnt[i]+=cnt[j];
    	for (int i=1e6;i>=1;i--)
    	{
    		cnt[i]*=cnt[i];
    		for (int j=i*2;j<=1e6;j+=i)
    			cnt[i]-=cnt[j];
    		if (cnt[i] && !pos[i]) return printf("-1"),0;
    	}
    	sort(a+1,a+1+n);
    	m=unique(a+1,a+1+n)-a-1;
    	for (int i=m;i>=1;i--)
    		for (int j=a[i]*2;j<=1e6;j+=a[i])
    			if (pos[j])
    			{
    				if (!fa[j]) fa[j]=a[i];
    				if (fa[j]%a[i]) return printf("-1"),0;
    			}
    	for (int i=2;i<=m;i++)
    		if (!fa[a[i]]) return printf("-1"),0;
    	for (int i=1;i<=n;i++)
    		if (pos[b[i]]!=i) cout<<pos[b[i]]<<" ";
    			else cout<<pos[fa[b[i]]]<<" ";
    	return 0;
    }
    
  • 相关阅读:
    hdu4405Aeroplane chess 概率dp水题
    【基础算法】排序-复杂排序之二(找出第K大的数)
    java代码实现输出指定以.java结尾的文件的绝对路径
    算法 Heap sort
    hdu2141Can you find it?
    c/c++:回调函数
    怎样在编译的时候,控制删除apk不用的资源?
    javascript原型的改动与重写(覆盖)区别
    深入理解Tomcat虚拟文件夹
    C++设计模式之代理模式
  • 原文地址:https://www.cnblogs.com/stoorz/p/15229013.html
Copyright © 2020-2023  润新知