大致题意: 给定三个数(a,b,c),把它们转化为无前导(0)的二进制数,然后以位数最高的数为基准,给其余数加前导(0)补齐。接下来让你将(a,b,c)二进制下各位重排得到(a',b',c'),使得(a'+b'=c'),求最小的(c')。
构造
一道构造题,大量分类讨论.......
我们设(ta,tb,tc)分别表示(a,b,c)二进制下(1)的个数,(l)表示最高的位数。
方便起见,我们强制(tale tb)。
显然,当(ta+tb=tc)时,答案在二进制下就是连续(tc)个(1)。
否则,我们考虑做二进制下加法,每一次进位,(1)的总数便会减少(1)个。
所以,如果(ta+tb<tc),就肯定无解。
不然,(ta+tb>tc),则我们设(t=ta+tb-tc),即所需进位的次数。
然后就是分类讨论了。
分类讨论
当(tage t)且(tbge t)时,我们可以从(a,b)中各拎出(t)个(1),把它们放在一起做加法,就实现了进位(t)次。这个运算的结果在二进制下就是(t)个连续的(1)和一个(0)。
然后,(a,b)中分别剩下了(ta-t)和(tb-t)个(1),由于我们不能让它再进位,所以就必须将它们错开做加法。这个运算的结果就是(ta+tb-2 imes t)个(1)。
也就是说,最后的答案将由(t)个连续的(1)和一个(0)、(ta+tb-2 imes t)个(1)两部分组成。
由于要让答案最小,所以我们尽量让(0)靠左。
而且,容易发现答案的总位数是((t+1)+(ta+tb-2 imes t)=ta+tb-t+1=tc+1)。
假设从右边开始首位为第(1)位,则最后答案中为(1)的区间就是:
当(ta<t)且(tbge t)时,首先把(a)中的(1)全部拎出来,再从(b)从同样拎出(ta)个(1)和它配合在一起做加法进位(ta)次。
但由于(ta<t),所以此时进位次数还不够,因此我们需要再从(b)中拎出(t-ta)个(1)放在这(ta)对(1)左侧,这样一来,(ta)对(1)经过加法进位后得到的最高位的那个(1)就会连带地让这(t-ta)个(1)全部进位。这个运算的结果在二进制下就是一个(1)、(t-ta)个(0)、(ta-1)个(1)和一个(0)。
而(tb)中我们一共拎出了(ta+(t-ta)=t)个(1),也就是还剩下(tb-t)个(1)。
同样容易发现,答案的总位数是(1+(t-ta)+(ta-1)+1+(tb-t)=tb+1)。
像前一种情况一样,由于要尽量让(0)靠左,所以最后答案中为(1)的区间就是:
当(ta<t)且(tb<t)时,发现如果按之前的方法去做就无法凑成(t)次进位。
假设我们需要从(a,b)中各拎出(x)个(1)让它们成对进位,而将(a,b)中剩下的(1)按照前面第二种情况中的方法连带进位,则就有:(x+(ta-x)+(tb-x)ge ta+tb-tc)。化简得到(xle tc)。
而根据(ta<t),即(ta<ta+tb-tc),得到(tb>tc),同理也能得到(ta>tc)。
所以,(x)一定能够取到(tc),且显然(x)越大答案越优(因为凑成的对数越多,答案的位数就越少)。
我们从(a,b)中各拎出(tc)个(1),然后把剩下的(ta+tb-2 imes tc=t-tc)个(1)一股脑地堆在这(tc)对(1)的左侧。
加法后,就得到一个(1)、(t-tc)个(0)、(tc-1)个(1)和一个(0)。
所以,最后答案中为(1)的区间就是:
顺便提一下,在之前的做法中有可能最后得到的(c')二进制下位数超过(l),显然与题意不符。
因此我们最后要判断(c'<2^l),才能输出答案。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define swap(x,y) (x^=y^=x^=y)
#define Fill(x,y) for(i=x;i<=y;++i) ans|=1<<(i-1)
using namespace std;
int a,b,c;
int main()
{
RI i,ta=0,tb=0,tc=0,l;scanf("%d%d%d",&a,&b,&c);
for(i=a;i;i&=i-1) ++ta;for(i=b;i;i&=i-1) ++tb;for(i=c;i;i&=i-1) ++tc;//统计1的个数
for(ta>tb&&swap(ta,tb),l=1;(1LL<<l)<=max(a,max(b,c));++l);//注意一定要1LL,否则2^31会挂掉
RI ans=0,t=ta+tb-tc;if(ta+tb==tc) {Fill(1,tc);goto End;}//以下是各种分类讨论
if(t<0) return puts("-1"),0;
if(ta<t&&tb<t) {Fill(2,tc);Fill(t+1,t+1);goto End;}
if(ta<t) {Fill(1,tb-t);Fill(tb-t+2,tb-t+ta);Fill(tb+1,tb+1);goto End;}
Fill(1,ta+tb-2*t);Fill(ta+tb-2*t+2,tc+1);
End:return ans<(1LL<<l)?printf("%d
",ans):puts("-1"),0;//最后输出答案前记得判断
}