bzoj3107 二进制a+b 构造

       日常orzPo爷的时候看到的。感觉挺好玩的就推了一下md发现自己智商不够。

       感觉举个例子真是简单易懂啊。下面的a,b,c均表示原题中a,b,c的位数:另外不妨令a<b。另外边界什么的问题好像这道题目里面不是很关键把就不考虑了。

       1.c<a<b,构造的例子如下(a=4,b=7,c=2):

0001111111

0110000011

1000000010

       令f(a,b,c)表示满足c<a<b时的最小位数。当c=1的时候,显然需要a+b+1位,即f(a,b,1)=a+b+c;否则,如果末位不是(1,1),那么位数就是f(a-1,b,c-1)+1或者f(a,b-1,c-1)+1;否则位数为f(a-1,b-1,c-1)+1;那么根据数学归纳法(只能宽泛地讲一下了),一定是f(a-1,b-1,c-1)最小,因此末位只能是(1,1)

       2.a<c<b,构造的例子如下(a=4,b=7,c=5):

00011110

01111111

10011101

       显然位数一定>b,那么位数为b+1一定是最优的,因此只能让第二个数的b个1在最后。假设第一个数的最后一位是第k位(从低到高),那么先把第k位加入,就变成100......0010000(假设k=5),然后加入的数显然是第k~k+a-2位才能让答案最小。那么现在考虑满足c的限制,发现第三个数是这样的10......01......101......1,然后我们知道有c个1,知道最高位是b+1,然后那个单独的0的位置是第c-a+1位,就好了。

       3.b<c<b+c,构造的例子如下(a=4,b=7,c=8):

011100001

011111110

111101111

       同样的显然位数>c(除非a+b=c),那么位数为c+1一定最优,于是我们要让0出现的尽可能早。注意到以第二个数为基准,第一个数的开头的1的个数就是a+b-c,那么我们让开头a+b-c全都是1即可。

       4.无解情况。。。。这个还用讲吗。。。

AC代码如下:

#include<cstdio>
#include<iostream>
using namespace std;

int calc(int x){
	int t=0; for (; x; x>>=1) t++; return t; 
}
int getit(int x){
	int t=0; for (; x; x^=x&-x) t++; return t;
}
int main(){
	int a,b,c; scanf("%d%d%d",&a,&b,&c);
	int len=max(max(calc(a),calc(b)),calc(c)),ans;
	a=getit(a); b=getit(b); c=getit(c); 
	if (a>b) swap(a,b);
	if (c<=a) ans=(1<<(a+b-c))|((1<<c)-2);
	else if (c<=b) ans=((1<<b)|((1<<c)-1))^(1<<(c-a));
	else if (c<=a+b) ans=((1<<(c+1))-1)^(1<<(c+c-a-b));
	else{
		puts("-1"); return 0;
	}
	printf("%d\n",(calc(ans)<=len)?ans:-1);
	return 0;
}


by lych

2016.4.7

这样能让0

你可能感兴趣的:(构造)