Codeforces Round 939 (Div. 2) D. Nene and the Mex Operator

文章讨论了一道Codeforces竞赛中的难题,涉及如何在给定数组中通过选择子数组并用其MEX值替换来最大化数组和。文章介绍了分析思路,使用DFS寻找最佳操作策略,并给出了详细解题代码示例。
摘要由CSDN通过智能技术生成

cf刷题日记

原题链接:Problem - D - Codeforces

样例输入1:

2
0 1

样例输出1:

4 1
1 2

样例输入2:

3
1 3 9

样例输出2

13 0

样例输入3:

4
1 100 2 1

样例输出3:

105 2
3 3
3 4

样例输入4:

1
0

样例输出4:

1 1
1 1

题目题意:

给你一个数组a,每次你可以选择数组的某一个子数组,然后用这个子数组的mex来替换这个子数组的所有元素,你可以最多执行5*10^5次该操作。题目求的是你可以得到的最大的数组a的和,操作数,以及具体的操作。(mex是该数组不包含的最小非负整数)

分析:

这个居然是cf的2000分的题目QAQ!!!,感觉分数有点虚高。

首先,如果有一个长度为x的数组,我们在对这个数组进行一系列的mex后,这个数组最大的和是x个x相加,即x*x,例如,如果x为5,我们先将这个数组变为全0,然后我们选择以下区间并对它进行mex操作:

1 1
1 2
2 2
2 2
1 3
2 3
2 2
2 3
3 3
3 3
1 4
2 4
2 2
2 3
3 3
3 3
2 4
3 4
3 3
3 4
4 4
4 4
1 5

经过这些操作和,这个数组全变为了5,并且,这个一定就是这个数组所能得到的最大值了。

然后,我们可以看到这个题目的n特别小,我们直接暴力求解,2^18才只有262144,用dfs找选择进行mex操作所有区间,每一个元素选与不选,0表示不选,1表示选。

举个列子,对于样例3,a数组为:1 100 2 1,我们dfs得到的最佳选择方法为0,0,1,1,就是只对[3,4],这个区间进行mex,最终得到了1,100,2,2。

注意,如果dfs得到的选择数组是1,0,1,1,那么要进行的mex操作的区间为[1,1],[3,4]。

这个题目代码如下:

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;

int n,a[105],s,m,qwq[105],q[105],ma;

vector<pair<int,int> > ans;

//用dfs来找最佳选择区间 
void dfs(int u)
{
	if(u==n+1)
	{
		vector<int> qaq;
		int cnt=0,sum=0;
		for(int i=1;i<=n;i++)
		{
			if(q[i]==0)//为0,这个位置的元素还是原来的值 
			{
				if(cnt)
				{
					qaq.push_back(cnt);
					cnt=0;
				}
				sum+=a[i];
			}
			else cnt++;//为1,进行了mex,这个位置的值取决于该位置的的区间长度 
		}
		if(cnt) qaq.push_back(cnt);
		for(int i=0;i<qaq.size();i++)
		{
			sum+=qaq[i]*qaq[i]; //加上进行mex后的值
		}
		if(sum>ma)//如果比当前的最大值还大,就记录该选择 
		{
			ma=sum;
			for(int i=1;i<=n;i++) qwq[i]=q[i];
		}
		return;
	}
	q[u]=0;
	dfs(u+1);
	q[u]=1;
	dfs(u+1);
}

//这个是该题目的难点,如何对一个区间进行一系列操作,来得到最大的值,用了一个递归 
void print(int l,int r,int u)
{
	for(int i=l;i<=r;i++)
	{
		ans.push_back(make_pair(l,i));
		if(i==u) return;
		if(l+1<=i) ans.push_back(make_pair(l+1,i));
		print(l+1,i,u);
	}
}

void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	dfs(1);
	
	int cnt=0;
	qwq[n+1]=0; 

	for(int i0=1;i0<=n+1;i0++)//注意,这里是从1到n+1,最后这个n+1可以很好的解决边界问题 
	{
		if(qwq[i0]) cnt++;
		else
		{
			if(cnt==0) continue;//如果为cnt为0,就不用就行mex 
			
			//先将这一段变为全0,方便操作 
			int l=i0-cnt,r=i0-1,te=0;
			for(int i=l;i<=r;i++) if(a[i]==0) te=1;
			
			//如果这一段的mex为0,就只用一次mex,否则两次 
			for(int i=0;i<=te;i++)
				ans.push_back(make_pair(l,r));
			
			//储存答案 
			print(l,r,r);
			cnt=0;
		}
	}
	
	//输出答案,大功告成!!! 
	cout<<ma<<" "<<ans.size()<<endl;
	for(int i=0;i<ans.size();i++)
		cout<<ans[i].first<<" "<<ans[i].second<<endl;
}

signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T=1;
//	cin>>T;
	while(T--) solve();
	return 0;
}

如果有看不懂的地方,可以评论提问,作者现役acmer,看到了问题就会回答,如果有错误的地方作者也很乐意改正^_^

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值