Codeforces Round 892 (Div. 2)补题

本文分析了三个Codeforces题目,涉及数组拆分保证元素互质、数组操作求和、自定义排列优化和区间问题的解决方案,以及使用动态规划解决最大值问题和斜率优化技巧。
摘要由CSDN通过智能技术生成

United We Stand(Problem - A - Codeforces)

题目大意:现有一个长为n的数组a,要拆成两个数组b,c,要求b中的任何一个元素不能整除c中的任何一个元素。

思路:我们只要将a中所有最小的挑出来,放入b,然后剩下的放入a就可以实现。

#include<bits/stdc++.h>
using namespace std;
int a[200010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		int mi=0x3f3f3f3f;
		for(int i=1;i<=n;i++) scanf("%d",&a[i]),mi=min(mi,a[i]);
		int c=0;
		for(int i=1;i<=n;i++)
		{
			if(a[i]==mi) c++;
		}
		if(c==n) printf("-1\n");
		else
		{
			printf("%d %d\n",c,n-c);
			for(int i=1;i<=c;i++) printf("%d ",mi);
			printf("\n");
			for(int i=1;i<=n;i++)
			{
				if(a[i]!=mi) printf("%d ",a[i]);
			}
			printf("\n");
		}
	}
}

Olya and Game with Arrays(Problem - B - Codeforces) 

题目大意:现在有若干个数组,我们只能从每个数组中移走一个数,但被移走的这些数可以放进任意一个数组。操作完成之后,我们求出每个数组的最小值,然后求和。

思路:显然,如果一个数组能够输出第二小的数,那么肯定比输出第一小的数要好,然后我们移走一个数组中最小的数后就可以实现输出第二小的数,但是这些被移走的数肯定得有一个地方放,而且最小的数无论被移到哪里都是会被输出的。所以一定有一个数组第二小的数没办法被输出,那么我们就找出所有第二小的数,然后从大到小排序,然后从前往后加,加到第n-1个,然后再加上所有数中最小的数即可。

#include<bits/stdc++.h>
using namespace std;
int a[200010];
bool cmp2(pair<int,int>a,pair<int,int>b)
{
	return a.second>b.second;
}
signed main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d",&n);
		vector<pair<int,int>>p;
		int mi=0x3f3f3f3f;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&m);
			for(int j=1;j<=m;j++) 
			{
				scanf("%d",&a[j]);
				//printf("%d\n",a[j]);
				mi=min(mi,a[j]);
			}
			sort(a+1,a+1+m);
			p.push_back({a[1],a[2]});
		}
		if(n==1) printf("%d\n",mi);
		else
		{
			long long sum=0;
			sort(p.begin(),p.end(),cmp2);//按第二维从大到小排
			for(int i=0;i<p.size()-1;i++) sum += (long long)p[i].second;
			sum += (long long)mi;
			printf("%lld\n",sum);
		}
	}
}

Another Permutation Problem(Problem - C - Codeforces

题目大意:现在限制排列的长度,要求自定义排列顺序,使得sum(a[i]*i)-max(a[i]*i)的结果最大,输出最大结果。

思路:我们首先看一下能找出哪些性质:
假设原来的数和序号一一对应,那么交换后,和会变小

在末尾进行交换操作,可以使被减掉的数变小。

所以第一个思路就是前面尽量不换,后面交换。具体执行是选一段区间逆转它,其实有猜的成分在里面,但是确实对于样例可以通过,对于题目也可以通过。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		int mx=0;
		for(int i=0;i<=n;i++)//选定保留长度
		{
			int sum=0,to=0;
			for(int j=1;j<=i;j++) sum += j*j,to=max(to,j*j);
			for(int j=i+1,z=n;j<=n;j++,z--)
			{
				sum += j*z;
				to=max(j*z,to);
			}
			mx=max(mx,sum-to);
		}
		printf("%d\n",mx);
	}
}

ps:这里有一点要注意,就是自己在推样例猜的时候一定要把变化算清楚,不然推出正确结果也会被判成否定的。

思路2:因为我们要减去最大值,所以我们就可以枚举规定最大值,然后找合法的排序,进而找出结果。我们可以将1-n的数记录在set中,每次从大到小,在集合中找大于i/j的第一位(i是我们枚举的最大值,j是我们当前找的位置),如果找到开头,就说明这个数不存在于集合当中 ,因为我们是向后找一位得到的。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		int mx=0;
		for(int i=1;i<=n*n;i++)
		{
			set<int>s;
			for(int j=1;j<=n;j++) s.insert(j);
			int sum=0;
			for(int j=n;j>=1;j--)
			{
				auto it=s.upper_bound(i/j);
				if(it==s.begin())//因为我们往后找一位,往后找一位找到开头,就说明使它成立的值不在集合当中,那么肯定判否
				{
					sum=0;
					break;
				}
				it--;
				sum += (*it)*j;
				s.erase(it);
			}
			mx=max(mx,sum-i);
		}
		cout<<mx<<endl;
	}
}

ps:这个思路的暗示就在于n<500,所以差不多是n^3的时间复杂度,然后枚举最大值的循环复杂度是n*n,给每个点找匹配logn,总共n个点,刚好是n*n*n*logn

Andrey and Escape from Capygrad(Problem - D - Codeforces)

题目大意:现在有若干个区间[l,r],每段区间上有一段核心区域[a,b],我们只要处在[l,r]上就可以跳到[a,b]上任意一点,现给出若干个查询,问在每个查询位置时,最远能到哪里。

思路:

这个题属于扫描线类型的题目,之前其实遇到过类似的,但是当时不知道那个思路的名字,只是觉得思路很妙。这个思路主要用来做多组区间多组单点查询的题目,点的结果与区间位置有关系,那么就可以看一看能不能将区间端点和待访问的点记录在一块儿,通过遍历,来动态找出答案

#include<bits/stdc++.h>
using namespace std;
int s[200010],ans[200010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		vector<pair<int,pair<int,int>>>q;
		for(int i=1;i<=n;i++)
		{
			int l,r,a,b;
			scanf("%d%d%d%d",&l,&r,&a,&b);
			q.push_back({l,{-1,i}});
			q.push_back({b,{1,i}});
		}
		int qt;
		scanf("%d",&qt);
		for(int i=1;i<=qt;i++)
		{
			int x;
			scanf("%d",&x);
			q.push_back({x,{0,i}});
		}
		sort(q.begin(),q.end());
		reverse(q.begin(),q.end());
		int hh=0,tt=0;
		for(int i=0;i<q.size();i++)
		{
			int v=q[i].first,type=q[i].second.first,idx=q[i].second.second;
			if(type==1) s[tt++]=v;
			else if(type==-1) tt--;
			else 
			{
				if(hh==tt) ans[idx]=v;
				else ans[idx]=s[hh];
			}
		}
		for(int i=1;i<=qt;i++) printf("%d ",ans[i]);
		printf("\n");
	}
}

 Maximum Monogonosity(Problem - E - Codeforces) 

题目大意:有两条线,线上各有n个节点,我们我们可以选定一段区间[l,r],然后这一段的价值就是abs(a[l]-b[r])+abs(a[r]-b[l]),这一段的长度定义为r-l+1。我们现在需要选出总长度不超过k的若干段区间,使得这些区间的价值最大。(这题用到斜率优化dp,等我学完来贴个博客链接)

思路:虽然这里要选的不是单个的物品,但实际上我们也可以用动态规划来做。

状态表示:dp[i][j]表示从前i个中选,长度恰好是j的价值,它的值表示最大价值。

状态计算:

不选第i个点:dp[i][j]=dp[i-1][j];

选第i个点:dp[i][j]=max(dp[i-x][j-x]+w[i-x+1,i])

w[i-x+1,i]表示从从i往前延伸长度为x的区间得到的价值,dp[i-x][j-x]表示从前i-x中选,消耗j-x个长度得到的最大值,至于x的长度,我们可以暴力枚举来做。

理论上是可以的,但是,这样时间复杂度太高了,而且这道题的数据范围又很大,那么我们就要进行优化。

我们先把选i点的情况展开:

dp[i][j]=max(

dp[i-x][j-x]+a[i]-b[i-x+1]+b[i]-a[i-x+1]

dp[i-x][j-x]-a[i]+b[i-x+1]+b[i]-a[i-x+1]

dp[i-x][j-x]+a[i]-b[i-x+1]-b[i]+a[i-x+1]

dp[i-x][j-x]-a[i]+b[i-x+1]-b[i]+a[i-x+1]

)

所以现在只需要把正负号的对应关系讨论清楚即可。

dp[i-x][j-x]-b[i-x+1]-a[i-x+1]+a[i]+b[i]

dp[i-x][j-x]+b[i-x+1]-a[i-x+1]-a[i]+b[i]

dp[i-x][j-x]-b[i-x+1]+a[i-x+1]+a[i]-b[i]

dp[i-x][j-x]+b[i-x+1]+a[i-x+1]-a[i]-b[i]

mx1[i-j]=dp[i-x][j-x]-b[i-x+1]-a[i-x+1]

mx2[i-j]=dp[i-x][j-x]+b[i-x+1]-a[i-x+1]

mx3[i-j]=dp[i-x][j-x]-b[i-x+1]+a[i-x+1]

mx4[i-j]=dp[][]+b[i-x+1]+a[i-x+1]

#include<bits/stdc++.h>
using namespace std;
int dp[3010][3010],mx1[3010],mx2[3010],mx3[3010],mx4[3010];
int a[3010],b[3010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,k;
		scanf("%d%d",&n,&k);
		memset(a,0,sizeof a);
		memset(b,0,sizeof b);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);
		memset(mx1,-0x3f,sizeof mx1);
		memset(mx2,-0x3f,sizeof mx2);
		memset(mx3,-0x3f,sizeof mx3);
		memset(mx4,-0x3f,sizeof mx4);
		for (int i=1;i<=n;++i)
			for (int j=1;j<=n;++j) dp[i][j]=-0x3f3f3f3f;
		dp[0][0]=0;
		mx1[0]=-a[1]-b[1]; mx2[0]=-a[1]+b[1]; mx3[0]=a[1]-b[1]; mx4[0]=a[1]+b[1];
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<=k;j++)
			{
				if(j>i) break;
				dp[i][j]=dp[i-1][j];
				dp[i][j]=max(dp[i][j],mx1[i-j]+a[i]+b[i]);
				dp[i][j]=max(dp[i][j],mx2[i-j]-a[i]+b[i]);
				dp[i][j]=max(dp[i][j],mx3[i-j]+a[i]-b[i]);
				dp[i][j]=max(dp[i][j],mx4[i-j]-a[i]-b[i]);
				if(i!=n)
				{
					mx1[i-j]=max(mx1[i-j],dp[i][j]-b[i+1]-a[i+1]);
					mx2[i-j]=max(mx2[i-j],dp[i][j]+b[i+1]-a[i+1]);
					mx3[i-j]=max(mx3[i-j],dp[i][j]-b[i+1]+a[i+1]);
					mx4[i-j]=max(mx4[i-j],dp[i][j]+b[i+1]+a[i+1]);
				}
				ans = max(ans,dp[i][j]);
			}
		}
		cout<<ans<<endl;
	}
}
抱歉,根据提供的引用内容,我无法理解你具体想要问什么问题。请提供更清晰明确的问题,我将竭诚为你解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【CodeforcesCodeforces Round 865 (Div. 2) (补赛)](https://blog.csdn.net/t_mod/article/details/130104033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值