spoj1182 Sorted bit squence/[USACO2003 Dec]Cow Queueing

题目链接:SPOJ的->http://www.spoj.com/problems/SORTBIT/

题目大意:

约翰给每头奶牛用一个数字编号,这些奶牛在集合时,会将自己编号转换成二进制表示,并按照以下规则排队:
• 首先,编号的二进制中1 出现次数较少的排在队伍的前面;
• 其次,如果1 的数量一样多,那么编号较小的排在前面;
举个例子,从4 到15,有12 个数字,顺序应该是
100; 1000; 101; 110; 1001; 1010; 1100; 111; 1011; 1101; 1110; 1111
假设编号在A到B之间的所有奶牛都在排队,那么这个队伍中排在第K 位的奶牛编号应该是多少呢?


注意:USACO的保证都是非负数0 ≤A≤B<10^9;而spoj上可能有负数但A,B同正同负 -2^31 ≤ A ≤ B ≤ 2^31-1,而且若为负数,则其二进制与其相反数的二进制的和为2^32。


题解:

组合+二分+数位DP

下面的都是最低位为第0位(还是1..???忘了..自己看)。

设f[i][j][k]表示在0~i中,做到了第j位(二进制),要放k个1有多少种方案(即有多少个这样的数)。

首先,如果i在这位上有1,那么我在这一位取0的话就不在上限边缘,就可以在j-1个位置上随便摆k个1,有C(j-1,k)种方案;而我若取了1,那就为f[i][j-1][k-1]。

而如果i在这位上是0的话,那我也只能取0了啊,所以为f[i][j-1][k]。

这个有什么用呢?

我们先找到排第K位的奶牛有几个1。知道↑之后不用说怎么做吧。

然后在范围[A,B]中二分,使排位不断接近K。因为是用左端点不断靠近答案,所以最后输出l。

USACO的输入输出都是二进制,而spoj的输入输出都是十进制(而且负数要搞回负数..(提醒了我负数这一回事

如果都是负数的话,因为其二进制与其相反数的二进制的和为2^32,那么我们把这个数+2^32,最后再减回来是一样的。

为什么呢?假设这个数为x,相反数为-x,相反数为正数所以是正常的...意思就是(-x)10=(-x)2诶就是说-x的二进制表示的大小和原数相同。

那么 有 -x +(x)2 = 2^32 --> (x)2=x+2^32。

就得到了它的二进制了。其他的照做就好了。 

p.s.下面的代码是spoj的。如果要交USACO的话就把多组数据删了,注释的弄回来,“=====”之间的注释掉。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;

LL bit[40],C[40][40];
char s[40];
const LL MX=1LL<<32;
void redc()//预处理组合C(i,j)
{
	C[0][0]=1;
	for (int i=1;i<=32;i++)
	{
		C[i][0]=1;
		for (int j=1;j<=i;j++)
		  C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
}
void dels(LL &x)//这是处理二进制输入的
{
	int len=strlen(s);x=0;
	for (int i=len-1,k=0;i>=0;i--,k++)
	 x+=bit[k]*(s[i]-'0');
}
LL dfs(LL x,int w,int k)//就是上面说的f[x][w][k]。不过没有存下来而已。意义一样
{
	if (w==0 && k==0) return 1;
	if (x<0 || w==0 || k<0) return 0;
	LL p=1LL<<(w-1);
	if (x&p) return C[w-1][k]+dfs(x,w-1,k-1);
	return dfs(x,w-1,k);
}
int main()
{
	//freopen("cowq.in","r",stdin);
	//freopen("cowq.out","w",stdout);
	int i,T;LL l,r,now,sum,K;
	LL A,B,mid,jl,ind;bit[0]=1;
	for (i=1;i<=32;i++) bit[i]=bit[i-1]*2;
	redc();
	scanf("%d",&T);
	while (T--)
	{
		// scanf("%s\n",s);dels(A);
		// scanf("%s",s);dels(B);
		//-----------------------
		scanf("%lld%lld",&A,&B);
		scanf("%lld",&K);
		bool pd=0;
		if (A<0 || B<0) A+=MX,B+=MX,pd=1;
		if (A>B) {LL t=A;A=B;B=t;}
		//------------------------
		sum=now=0;
		for (i=0;i<=31;i++)
		{
			now=dfs(B,32,i)-dfs(A-1,32,i);
			if (sum+now<K) {sum+=now;ind=i;}
			else {K=K-sum;break;}
		}ind++;//找排第K的有ind个1
		l=A;r=B;jl=dfs(A-1,32,ind);
		while (l<r)//二分
		{
			mid=(l+r)>>1;
			now=dfs(mid,32,ind)-jl;
			if (now<K) l=mid+1;
			else r=mid;
		}
		//-----------------------
		if (pd) l=l-MX;
		printf("%lld",l);
		//-----------------------
		// bool pd=0;
		// for (i=32;i>=0;i--)
		 // if (l&bit[i])
		 // {
			// printf("1");
			// pd=1;
		 // }else if (pd) printf("0");
		printf("\n");
	}
	return 0;
}


posted @ 2016-11-04 10:49  OxQ  阅读(277)  评论(0编辑  收藏  举报