题意
给你一个序列,长度为
n
n
n,你每次操作可以选择任意一个区间,并给区间里面每个数加上不超过
k
k
k的正整数,让你用最少的操作使得序列变成回文。
1
<
=
k
<
=
3
,
n
<
=
1
0
6
1<=k<=3,n<=10^6
1<=k<=3,n<=106,特别地,当
k
=
3
,
n
<
=
1000
k=3,n<=1000
k=3,n<=1000
Solution
这道题当时在考场上就觉得挺有趣的,感觉应该能够自己想出来。最近几天补题发现比我想象的要难上许多,想了快一天才想出正确的结论。
先简化题意,我们转化成差分数组,差分数组的长度为 n − 1 n-1 n−1,区间修改就是选择一前一后的两个数,前面的数加上 t ( 1 < = t < = k ) t(1<=t<=k) t(1<=t<=k),后面的数减去 t t t,也可以选择只给一个数加上或减去 t t t。
我们不妨设一开始的数组为 a i ( 1 < = i < = n ) a_i(1<=i<=n) ai(1<=i<=n),差分数组为 b i = a i + 1 − a i ( 1 < = i < n ) b_i=a_{i+1}-a_i(1<=i<n) bi=ai+1−ai(1<=i<n)。
同样地,回文就是让每一对 b i b_i bi和 b n − i b_{n-i} bn−i变成一对相反数,也就是让 b i + b n − i 2 \frac{b_i+b_{n-i}}{2} 2bi+bn−i变成 0 0 0,因此作用在某一对上的操作要么都是增加,要么都是减少,我们可以很容易算出要增加或者要减少的数。
继续观察,发现虽然每个操作的加和减都有前后顺序,但是不同对之间存在包含关系,因此只要在每对的两个里面选择恰当的位置,就可以让某一对减少另外一对增加。
由此,题意转化为:给你两个数组(把原来正的和负的分开),你每次操作选择一个 t ( 1 < = t < = k ) t(1<=t<=k) t(1<=t<=k),可以选择两种方式操作:1.给某个 > = t >=t >=t的数减去 t t t;2.给不同数组中两个 > = t >=t >=t的数都减去 t t t。
然后就分类讨论k。
k=1
很简单,只需要求出两个数组的总和,取大的那一个
k=2
首先,肯定是能都减2就都减2,不行就都减1,这时尽量让此时总和大的一边剩下尽可能多的偶数,然后有一边全部变成0,另外一边能减2就减2,剩下的-1。
k=3
想了很久都没有思路。一开始以为能都减3就都减3,但是这样不对,反例:4 | 3 2 2 ,这样应该把4拆成两个2,然后把3单独匹配。不过想了很久以后想到了很重要的一点,两边选择的2个数非常少。某一边选择的2,无非就是直接减去,或者和另外一边的2匹配,那么第一种最多一个,否则就可以减3再减1。匹配的2不会把其中一边覆盖两次。自己模拟了一些数据感觉每一边选出的2都不会超过数的总量,即500(前面的配对缩减了一半)。
因此我们就可以枚举 O ( n 2 ) O(n^2) O(n2)枚举两边选择的2的个数(包括匹配的和减去的),然后我们给两边分别dp一下求出选出了这么多的2以后最多能够剩余多少个3。这样以后就只需考虑只有1和3的情况了,这个思路和 k = 2 k=2 k=2基本一样,随便搞一下就行了。
代码
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
x=0;int f=0;char s=getchar();
while(!isdigit(s))f|=s=='-',s=getchar();
while(isdigit(s))x=x*10+s-48,s=getchar();
x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
if(x<0)putchar('-'),x=-x;
do{buf[++cc]=int(x%10);x/=10;}while(x);
while(cc)putchar(buf[cc--]+'0');
}
const int N=1e6+10,M=1e3+10;
int n,k;ll a[N],b[N];
int cnt1;ll c1[N];//+
int cnt2;ll c2[N];//-
ll dp1[M][M],dp2[M][M];
void solve(){
qr(n),qr(k);
if(n==1){
puts("0");
return;
}
rep(i,1,n)qr(a[i]);
rep(i,1,n-1)b[i]=a[i+1]-a[i];
n--;
rep(i,1,n/2){
ll now=b[i]+b[n+1-i];
if(now==0)continue;
if(now<0)c1[++cnt1]=-now;
if(now>0)c2[++cnt2]=now;
}
if(n&1){
int p=b[n/2+1];
if(p<0)c1[++cnt1]=-p;
if(p>0)c2[++cnt2]=p;
}
if(k==1){
ll s1=0,s2=0;
rep(i,1,cnt1)s1+=c1[i];
rep(i,1,cnt2)s2+=c2[i];
qw(max(s1,s2));puts("");
}
if(k==2){
ll s1=0,s2=0;
rep(i,1,cnt1)s1+=c1[i]/2;
rep(i,1,cnt2)s2+=c2[i]/2;
if(s1<s2){
swap(s1,s2);
swap(cnt1,cnt2);
rep(i,1,max(cnt1,cnt2))swap(c1[i],c2[i]);
}
ll ans=s2,s3=0;
rep(i,1,cnt2){
c2[i]%=2;
if(c2[i]==1)s3++;
}
rep(i,1,cnt1){
if(!s2)break;
ll now=min(c1[i]/2,s2);
c1[i]-=now*2;
s2-=now;
}
rep(i,1,cnt1){
if(!s3)break;
if(c1[i]&1){
c1[i]--;
s3--;
ans++;
}
}
rep(i,1,cnt1){
ll now=min(c1[i]/2,s3/2);
c1[i]-=now*2;
s3-=now*2;
ans+=now*2;
}
if(s3==1){
rep(i,1,cnt1){
if(c1[i]){
c1[i]--;
s3=0;
ans++;
break;
}
}
}
ans+=s3;
rep(i,1,cnt1){
if(c1[i]&1){
c1[i]--;ans++;
}
ans+=c1[i]/2;
}
qw(ans);puts("");
}
if(k==3){
int tt=cnt1+cnt2;
rep(i,1,tt)dp1[0][i]=-1;
rep(i,1,cnt1){
rep(j,0,tt)dp1[i][j]=-1;
rep(j,0,tt){
if(2ll*j>c1[i])break;
ll now=(c1[i]-2ll*j)/3;
rep(k,j,tt){
if(dp1[i-1][k-j]!=-1){
dp1[i][k]=max(dp1[i][k],dp1[i-1][k-j]+now);
}
}
}
}
rep(i,1,tt)dp2[0][i]=-1;
rep(i,1,cnt2){
rep(j,0,tt)dp2[i][j]=-1;
rep(j,0,tt){
if(2ll*j>c2[i])break;
ll now=(c2[i]-2ll*j)/3;
rep(k,j,tt){
if(dp2[i-1][k-j]!=-1){
dp2[i][k]=max(dp2[i][k],dp2[i-1][k-j]+now);
}
}
}
}
ll tot1=0,tot2=0;
rep(i,1,cnt1)tot1+=c1[i];
rep(i,1,cnt2)tot2+=c2[i];
ll ans=1e18;
rep(i,0,tt){
if(dp1[cnt1][i]==-1)continue;
rep(j,0,tt){
if(dp2[cnt2][j]==-1)continue;
ll s1=tot1-dp1[cnt1][i]*3-2*i,s3=dp1[cnt1][i];
ll sum1=tot2-dp2[cnt2][j]*3-2*j,sum3=dp2[cnt2][j];
ll ret=max(i,j);
ll t=min(s3,sum3);
s3-=t,sum3-=t,ret+=t;
t=min(s1,sum1);
s1-=t,sum1-=t,ret+=t;
if(!s1&&!s3)ret+=sum1+sum3;
else if(!sum1&&!sum3)ret+=s1+s3;
else{
s1=max(s1,sum1);
s3=max(s3,sum3);
s3*=3;
ret+=s1;
s3-=s1;
if(s3>0)ret+=(s3+2)/3;
}
ans=min(ans,ret);
}
}
qw(ans);puts("");
}
}
int main(){
// freopen("A.in","r",stdin);
// freopen("A.out","w",stdout);
int tt;tt=1;
while(tt--)solve();
return 0;
}