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