• POI 2012 ODL-Distance


    POI 2012 ODL-Distance

    洛谷传送门

    给你一个序列a[],定义d(i,j)=a[i],a[j]每次操作可以对其中之一乘一个质数p或除以一个数p(p必须为被除数的约数),让a[i]=a[j]的最少操作步数

    对于每个i,求d(i,j)最小的j,若有多个解,输出最小的j


    题解:

    其实这道题的性质啥的还是比较好推导的,我觉得难点只在于如何简洁、正确地实现代码。

    先考虑:对于任意的两个数(A,B),如何用最少的乘除质数的操作完成转换?

    肯定秒想质因子分解啊。

    现在我们有A的质因子,B的质因子,A,B的公共质因子。明显地,最少步数就是相同的质因子保留,不同的质因子乘除,设(f(x))(x)的质因子个数(质因子指数和)。那么最少步骤就是:(f(A)+f(B)-2f(gcd(A,B)))

    这个式子还是很好推导的。

    然后显而易见的暴力思路就是对于每个位置(i),从头到尾枚举(j),一旦符合条件就跳出。(O(n^2))做法。

    肯定是不行,我们思考优化。对于这种三元问题,定一元,枚举另一元,不行的话就枚举下一元好了。既然枚举(j)要T,我们尝试枚举(gcd(a[i],a[j])=g)

    那么对于(g),我们需要在它的所有倍数中找到存在于序列中的且下标尽可能小的位置。这个步骤不能用正向思维,比如用set维护。要逆向想:在枚举一个数(x)的所有因子(g)的过程中,(x)就是(g)的一个倍数,我们直接用(x)来更新就好了。

    需要注意的是,有可能存在最小下标就是这个数本身的情况,于是我们还需要维护一个次小值。

    最后查询的时候直接(O(1))就好。

    代码:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn=1e6+5;
    const int maxw=1e6+6;
    const int INF=1e9;
    int n,tot,ans,val,maxx;
    int a[maxn],prime[maxn],cnt[maxn],mn1[maxn],mn2[maxn];
    //mn1,mn2表示一个数的所有倍数中最小、次小值的位置
    bool v[maxn];
    void init(int x)
    {
        for(int i=2;i<=x;i++)
        {
            if(!v[i]) 
    			prime[++tot]=i,cnt[i]=1;
            for(int j=1;j<=tot && i*prime[j]<=x;j++)
            {
                v[i*prime[j]]=1;
    			cnt[i*prime[j]]=cnt[i]+1;
                if(i%prime[j]==0) 
    				break;
            }
        }
    }
    void update(int d,int x)
    {
        if(cnt[a[x]]<cnt[a[mn1[d]]]) 
    		mn2[d]=mn1[d],mn1[d]=x;
        else if(cnt[a[x]]<cnt[a[mn2[d]]]&&x!=mn1[d]) 
    		mn2[d]=x;
    }
    void getans(int d,int x)
    {
        int pos=mn1[d]==x?mn2[d]:mn1[d];
    	int v=cnt[a[x]]+cnt[a[pos]]-2*cnt[d];
        if(v<val||(v==val&&pos<ans)) 
    		val=v,ans=pos;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		maxx=max(maxx,a[i]);
    	}
        init(maxx);
    	cnt[0]=INF;
        for(int i=1;i<=n;i++)
            for(int j=1;j*j<=a[i];++j)
            {
                if(a[i]%j) 
    				continue;
                update(j,i);
    			update(a[i]/j,i);
            }
        for(int i=1;i<=n;++i)
        {
            val=INF;
            for(int j=1;j*j<=a[i];++j)
            {
                if(a[i]%j) 
    				continue;
                getans(j,i);
    			getans(a[i]/j,i);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Atitit 趋势管理之道 attilax著
    Atitit 循环处理的新特性 for...else...
    Atitit 2017年的技术趋势与未来的大技术趋势
    atitit 用什么样的维度看问题.docx 如何了解 看待xxx
    atitit prj mnrs 项目中的几种经理角色.docx
    Atitit IT办公场所以及度假村以及网点以及租房点建设之道 attilax总结
    Atitit 工具选型的因素与方法 attilax总结
    Atitit.团队文化建设影响组织的的一些原理 法则 定理 效应 p826.v4
    Atiitt 管理方面的误区总结 attilax总结
    Atitit 未来趋势把控的书籍 attilax总结 v3
  • 原文地址:https://www.cnblogs.com/fusiwei/p/14035866.html
Copyright © 2020-2023  润新知