CF1956D

题目传送门

CF1956D

题意

给定一个序列,定义一次操作为将区间 [ l , r ] [l,r] [l,r] 改为此区间的 m e x mex mex 值,求最后序列最大值以及操作方法。

思路

看到条件,首先考虑理论最大值。

令区间为 [ l , r ] [l,r] [l,r],则长度为 l e n = r − l + 1 len=r-l+1 len=rl+1,理论可以让整个序列变成 0 , 1 , 2 , ⋯   , l e n − 1 0,1,2,\cdots,len-1 0,1,2,,len1。最后变一次,让整个序列全变成 l e n len len,值为 l e n 2 len^2 len2

接下来证明。考虑构造序列。

我们在区间里取两个数,对他们本身进行操作,最多通过两次操作使它们变成 0 0 0

接下来对其中一个数进行操作,使它变成 1 1 1

再对这两个数所在区间进行操作,使它们变成 2 2 2

以此类推,可以将数列里所有数变成 l e n len len,和为 l e n 2 len^2 len2

现在已经处理好最大值,设 [ l , r ] [l,r] [l,r] 这个区间答案为 a n s ans ans,考虑最后答案。

由于可以不进行操作,所以我们要与 a n s ans ans m a x max max,即 m a x ( a n s 2 , ∑ i = l r a i ) max(ans^2,\sum_{i=l}^{r}{a_i}) max(ans2,i=lrai)

这个式子可以使用区间 dp 维护,枚举断点即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
inline int read(){
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int maxn=20;
int n,mx;
int a[maxn];
bool f[maxn];
vector<pair<int,int>> res,g;
void cal(int l,int r){
	if(l==r){
		if(a[l]!=0) a[l]=0,res.push_back({l,l});
		return;
	}
	if(a[r]==r-l){
		cal(l,r-1);
		return;
	}
	cal(l,r-1),res.push_back({l,r});
	for(int i=l;i<=r;i++) a[i]=r-l;
	cal(l,r-1);
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=0;i<(1<<n);i++){
		for(int j=1;j<=n;j++) f[j]=(i>>(j-1))&1;
		int l=0,r=-1,cnt=0;
		vector<pair<int,int>> p;
		for(int j=1;j<=n;j++){
			if(f[j]){
				if(f[j-1]) r=j;
				else l=r=j; 
			}else{
				cnt+=(r-l+1)*(r-l+1);
				if(l!=0) p.push_back({l,r});
				l=0,r=-1,cnt+=a[j];
			}
		}
		if(f[n]==1&&l!=0) p.push_back({l,r}),cnt+=(r-l+1)*(r-l+1);
		if(cnt>mx) mx=cnt,g=p;
	}
	for(auto ed:g) cal(ed.first,ed.second),res.push_back({ed.first,ed.second});
	cout<<mx<<" "<<res.size()<<endl;
	for(auto ed:res) cout<<ed.first<<" "<<ed.second<<endl;
	return 0;
}

提交记录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值