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;
}


原文地址:https://www.cnblogs.com/PPXppx/p/10371558.html