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");
}
}