题目传送门
题意
给定一个序列,定义一次操作为将区间 [ 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=r−l+1,理论可以让整个序列变成 0 , 1 , 2 , ⋯ , l e n − 1 0,1,2,\cdots,len-1 0,1,2,⋯,len−1。最后变一次,让整个序列全变成 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;
}