做题小结 dp训练2

第一个

这道题考察了数论 但是很奇葩的是他只是要求前后gcd不是1就行了

所以 我们可以想到fi+1是可以直接继承fi的 如果他们俩gcd>1

然后观察了数据 这个暴力做不了的 不过牵涉到gcd不就两个做法

一个根号n的做法 求约束 另一个就是埃氏的loglog写法
本题考查后者 前者不适合 因为 我们两者如果gcd大于1 但是
数列可能不会出现我们两都有的那个约数 就无法从那个f约数进行转移了

数列给出的值完全随机呗

所以用后者就行了 对于一个ai包括他的所有质数 也就是他的约数 然后在所有的约数取一个最大值并且修改所有f值就行了 因为我们说了只要二者有gcd就可以进行继承 所以可以修改f值 然后开一个dp数组来记录最大的答案即可


for (int i = 1; i <= cnt; i++) {
		for (int j = prime[i]; j <= 100005; j += prime[i]) {
			v[j].push_back(prime[i]);
		}
	}
	for (int i = 1; i <= n; i++) {
		int temp = 0;
//		int w=v[a[i]].size();
		for (auto j : v[a[i]]) {
           temp=max(temp,f[j]);
		}
       dp[i]=temp+1;
		for(auto j:v[a[i]])
		{
			f[j]=max(f[j],dp[i]);
		}		
	}

下一个

这也是一个很好的dp题 考察了离散思想
非常good 给了我新的dp建立数组的思路 因为n比较小
还有从题目推出格位这个也有的思考的 我还没想到呢
这个q是一个定值 我当时就是在想是不是

然后dp数组是这样的 dp[i][a[j]=dp[j][a[i]]+1
需要离散
哇咔咔真是好题一道

离散后的aj ai就是一个下标 刚好n很好小 于是n^2可做 同时数组也存的下 +1是因为我们每次没有包括i作为序列的尾巴

	cin>>n;int ans=0;
   for(int i=1;i<=n;i++)cin>>a[i];
   memcpy(b,a,sizeof a);
   sort(b+1,b+1+n);
	int len=unique(b+1,b+1+n)-(b+1);
	for(int i=1;i<=n;i++)
		a[i]=lower_bound(b+1,b+1+len,a[i])-b;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			dp[i][a[j]]=dp[j][a[i]]+1;
			ans=max(dp[i][a[j]],ans);
		}
	}
	cout<<ans+1<<endl;

下一个dp好题

这也是一个很好的dp 我第二次看还是没发现思路出来

牵扯到一个变形
如果sumj-sumi-1=j-i+1符合式子
变形下得到sumj-j=sumi-i
于是就可以了
开一个桶记录 非常好的一个题 牵涉到一点换算 很适合我

#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
#define debug cout<<endl<<"----------"<<endl;
using namespace std;
const int range=3e5+10;
int n;
int a[range];
int sum[range];
//CF93 DIV2 C 
void solve()
{
	cin>>n;
	map<int,int>ma;
	string s;
	cin>>s;
	s=' '+s;
	for(int i=1;i<=n;i++)
	{
//		cin>>a[i];
		int w=s[i]-'0';
		a[i]=w;
	}
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+a[i];
	}
	int ans=0;
	ma[0]=1;
	for(int i=1;i<=n;i++)
	{
		ans+=ma[sum[i]-i];
		ma[sum[i]-i]++;
	}
	cout<<ans<<endl;
	for(int i=1;i<=n;i++)sum[i]=0;
}

下一个

我当时写了一个约数还是什么的做法 反正超时了

for(int i=1;i<=n;i++)cin>>a[i],maxn=max(a[i],maxn);
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++)
	{
		for(int k=1;k*k<=a[i];k++)
		{
			if(a[i]%k==0)
			{
				v[i].push_back(k);
				if(a[i]/k!=k)
				v[i].push_back(a[i]/k);	
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int w=0;
		for(auto j:v[i])
		{
			w=max(w,dp[j]);
		}
		dp[a[i]]=w+1;
		ans=max(ans,dp[a[i]]);
	}

这题是不允许根号的 因为n=1e6 约数不行 考虑埃氏写法
于是就写出来了 采取一种A[I]只对倍数有贡献的思想 即可

	for(int i=1;i<=n;i++)
	{
			scanf("%d",&a[i]);
		dp[a[i]]+=1;
		for(int x=2;x*a[i]<=1e6;x++)
		{
//			dp[x*a[i]]=dp[a[i]];写错了
			dp[x*a[i]]=max(dp[a[i]],dp[x*a[i]]);
		    ans=max(ans,dp[x*a[i]]);
		}
		ans=max(ans,dp[a[i]]);
	}

posted @ 2024-08-09 18:34  LteShuai  阅读(19)  评论(0)    收藏  举报