AtCoder Petrozavodsk Contest 001 B题解

AtCoder Petrozavodsk Contest 001 B题解

题意

两个数组a和b,可以做这样的操作
选择一个数i和j
向ai加2,向bj加1。
问有限次操作后能否使a与b完全相等。

笺释

显然如果一开始a的和就比b大,那么一定不能达到。
如果一开始a小于等于b,考虑这这样一种手段:
对于每一个i,我们知道最终一定要让ai==bi,那么我们在拿到i的时候就进行操作让ai等于bi。

如果ai正好等于bi,我们不进行操作。
如果ai小于bi,如果ai和bi相差为2的倍数,我们只需要对ai进行n次操作(n=(bi-ai)/2),然后拿到的本来应该加到bj上的1存起来。如果ai和bi相差为2k+1,我们就先对ai和bi进行一次操作,将他们化为相差2的情况,再将ai化为bi,将多余的1存起来。
如果ai大于bi,我们把之前存起来的1拿出来加到bi上,将ai和bi化为相等。

最后如果我们存起来的1是负值,就意味着不能达到。如果存起来的1是正值或0,就意味着能达到。(如果是正值的话,只需要将其加到任何一个bi上,因为这时候ai与bi相等,也能够在有限次操作内将ai和bi再次化为相等)

这种算法的正确性证明

首先说明这一点,尽管目标序列不是唯一的(显然),但是由初始序列达到目标序列所需的操作数是唯一的。(定理1)

定理1证明

设目标序列1 A1,B1和目标序列2 A2,B2,初始序列A3,B3,由初始序列达到目标序列1步骤数为N,由初始序列达到目标序列2步骤数为M,设N>M,那么我们对目标序列2再进行(M-N)次操作,得到A2_与B2_,因为SUM(A2)==SUM(B2),SUM(A2+2(M-N))>SUM(B2+(M-N)),因此A2_与B2_不可能为目标序列,又因为SUM(A1)==SUM(A2_),SUM(B1)==SUM(B2_),因此SUM(A2_)!=SUM(B2_),与A1,B1为目标序列不符。因此定理1得证。

设一开始的a为A1 一开始的b为B1
最后要达到的a为A2 最后达到的b为B2(A2==B2)
我们已经说明了,通过这种手段,如果存起来的1是非负值,我们就一定能从A1达到目标态A2,也就是说,我们已经说明了,如果能够通过这种方式达到,那么一定可以。接下来只需要说明,如果这种方式达不到,那么一定不可以。
假设最终达到的状态(不可行)为A5 B5,此时,序列已经读到末尾,存起来的1是负值。为了取消掉存起来的1(因为这个假设本身是虚拟的),我们从第一个相等的数字a1和b1这一对中的b1中取出来sumone(此时亏欠的1)个,本来SUM(A5)==SUM(B5),这样取之后SUM(A5)>SUM(B5),而我们知道每一次操作SUM(A5)加2,SUN(B5)加1,继续向后操作一定不可行,也就意味着在前面的操作过程中有过SUM(A5)==SUN(B5)的状态,但是当时没有停止,没有停止也就意味着有不相等的ai和bi,我们最终要达到的是SUM(A5)==SUM(B5)且ai==bi(对于所有的i而言)这样的状态,那么要到达SUM(A5)==SUM(B5)的状态的操作数是确定的N,假设说存在着其他算法能够在N次操作内让ai==bi(对于所有的i),因为我们的算法Bi和Ai是尽可能小的,对于其他算法的任何一个bi而言,都满足Cbi>=Dbi,又因为SUM(CB5)==SUM(DB5),因此对于每一个bi而言都蛮子Cbi==Dbi==Dai,同理得到Cai==Dai==Cbi,与已知(存在不相等的ai和bi)矛盾。

=。=写这个的时候头发昏了,不知道定理1我要证来干嘛了,总之这样就多多少少说明了一点吧…大概…我猜。=/=。

完整代码

#include<bits/stdc++.h>
#define MAXN 10005
using namespace std;
long long ans1,ans2,sumone;
int tem,n,a[MAXN],b[MAXN];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
       scanf("%d",&a[i]);
       ans1+=a[i];
    }
    for(int i=1;i<=n;i++)
    {
       scanf("%d",&b[i]);
       ans2+=b[i];
    }
    if(ans1>ans2)
    {
        printf("No\n");
        return 0;
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            if(a[i]==b[i])
            {
                continue;
            }
            //单调的时候有一种保证最优的手段:积累1
            if(a[i]<b[i])
            {
                if((a[i]&1)&&(b[i]&1))
                {
                    sumone+=(b[i]-a[i])/2;
                }
                if((!(a[i]&1))&&(!(b[i]&1)))
                {
                    sumone+=(b[i]-a[i])/2;
                }
                if((a[i]&1)&&(!(b[i]&1)))
                {
                    sumone+=(b[i]+1-(a[i]+2))/2;
                }
                if((!(a[i]&1))&&(b[i]&1))
                {
                    sumone+=(b[i]+1-(a[i]+2))/2;
                }
                continue;
            }
            if(a[i]>b[i])
            {
                sumone-=a[i]-b[i];
            }
        }
        if(sumone<0)
        {
            printf("No\n");
            return 0;
        }
            printf("Yes\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值