zoukankan      html  css  js  c++  java
  • CF264B Good Sequences(DP+质数)

    传送门

    题意:求一个最长的序列,满足序列严格单调递增,任意相邻两个数非互质且所有的数都来自长度为n且单调递增的序列a三个条件.

    分析:既然原序列本身满足单调递增,所以对于这个条件我们可以不管.所有的数都来自a序列,额,这个条件更没必要管.所以我们从序列相邻两个数非互质入手.

    设f[i]表示以a[i]结尾的合法序列的最大长度,转移方程不难想到:(f[i]=max(f[j])+1),其中(gcd(a[i],a[j])>1)

    但是如果O(n)枚举a[i],然后每一次转移的时候让当前的a[i]O(n)地跟所有数比较一边,判断是否满足非互质,是否最优,总的时间复杂度是(n^2),肯定超时.

    注意到本题a1,a2...an<=(10^5),既然数值这么小(数组都能开到这么大),我们就可以考虑在ai上做文章.两个数x,y非互质,即gcd(x,y)>1,即x和y有相同的质因子,所以我们可以预处理出(10^5)内所有的质数.

    然后直接1-n枚举序列a,假设当前已经考虑到了ai这个数,我们再枚举所有预处理出的质数,如果当前的a[i]能被当前枚举到的这个质数pri[j]整除,则f[i]一定只能从f[k]转移过来,其中a[k]也能被pri[j]整除且k<i.

    所以我们可以开一个vector队列q,q[pri[j]]存的是序列a中所有能被pri[j]整除的数的下标,因为我们从小到大枚举a[i],而q[pri[j]]是随着我们的枚举才会不断更新的,所以q[pri[j]]里面的下标肯定是单调递增的.

    根据q数组的递增性和题目要求的最优性,如果f[i]可以被pri[j]整除,且q[pri[j]].size()不为零(即在之前的枚举中已经找到了至少一个k,使得a[k]也能被pri[j]整除),则f[i]一定是从q[pri[j]]中的最后一个数(q[pri[j]][q[pri[j]].size()-1])转移过来.

    每一次转移完之后别忘了更新q数组.

    P.S.我知道我上面的分析讲得很不清楚,就这么几句话挤了半个多小时才写出来,可能是因为我也不理解这道题吧,所以真的写的很烂...

    const int MAXN=100005;
    int n,m,ans;
    int a[MAXN],v[MAXN],pri[MAXN],f[MAXN];
    vector<int> q[MAXN];
    //q[i]记录的是序列a中所有能被i整除的数的下标
    //线性筛素数
    void get_prime(){
        v[1]=1;
        for(int i=2;i<=MAXN;i++){
    		if(!v[i]){
    	    	pri[++m]=i;
    	    	v[i]=i;
    		}
    		for(int j=1;j<=m;j++){
    	    	if(pri[j]>v[i]||i*pri[j]>MAXN)
                	break;
    	    	v[i*pri[j]]=pri[j];
    		}
        }
    }
    int main(){
        get_prime();
        n=read();
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=n;i++){//枚举序列a
    		int now=a[i];
    		for(int j=1;j<=m;j++){//枚举所有质数
    	    	if(now%pri[j]==0){
    //如果now能够被pri[j]整除,a[i]肯定也能被pri[j]整除
    				while(now%pri[j]==0)now/=pri[j];
    //这里与下面代码中if(now<pri[j])break;相照应
    //是为了优化时间
    				int cnt=q[pri[j]].size();
    				if(cnt)f[i]=max(f[i],f[q[pri[j]][cnt-1]]+1);
    //如果cnt不为零,即之前已经存在a[k]能够被pri[j]整除
    //则f[i]可以从q[pri[j]]中的最后一个数转移
    				else f[i]=max(f[i],1);
    //否则,说明之前枚举的所有的a[k]
    //与当前的a[i]的共同的质因子不包括pri[j]
    //即f[i]无法从前面转移过来
    				q[pri[j]].push_back(i);
    //既然当前的a[i]能被pri[j]整除,就可以更新q[pri[j]]
    	    	}
    	    	if(now<pri[j])break;
    //如果now已经比当前的pri[j]小,
    //pri数组里的质数肯定是单调递增的,
    //后面就更不会有比now还小的质数了,
    //即后面肯定不存在j,使得now%pri[j]=0了,直接退出
    		}
        }
        for(int i=1;i<=n;i++)ans=max(ans,f[i]);
        if(!ans)puts("1");//序列只有一个数显然成立
        else printf("%d
    ",ans);
        return 0;
    }
    
    
    
  • 相关阅读:
    Centos6.8部署jumpserver(完整版)
    Linux系统下inode满了导致无法写文件的解决思路
    MySQL5.7.20报错Access denied for user 'root'@'localhost' (using password: NO)
    深浅拷贝
    集合
    列表 元组 range
    机器码和字节码
    函数
    文件操作
    小数据池和代码块
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10371558.html
Copyright © 2011-2022 走看看