thucampday1 A. Palindromization

题意

给你一个序列,长度为 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 n1,区间修改就是选择一前一后的两个数,前面的数加上 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+1ai(1<=i<n)

同样地,回文就是让每一对 b i b_i bi b n − i b_{n-i} bni变成一对相反数,也就是让 b i + b n − i 2 \frac{b_i+b_{n-i}}{2} 2bi+bni变成 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值