SPOJ 1182 Sorted bit sequence

题目链接

题意:

分析:

其实如果会了Ural 1057. Amount of Degrees那道题目,这道题自然也就会了...

我们考虑枚举第$k$个数字的$1$的个数,那么我们需要计算的也就是区间内二进制状态下$1$的个数为$x$的数字个数,这个的求法在上一题中写过了...

我们求到第$k$的数字的$1$的个数为$x$,那么我们去二分这个数字是什么,也就是说我们要求一个最靠左的右端点,使得区间$[n,ans]$内$1$的个数为$x$的数字个数恰好为$k$,然后总体思路就解决了...

细节方面就是要注意特判$0$,然后对于负数的处理就是先去掉负数的符号位,然后判断,最后输出的时候再把符号位加上...

具体实现看代码...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=32+5;

int n,m,k,ans,cas,f[maxn][maxn];

inline void init(void){
	f[0][0]=1;
	for(int i=1;i<=31;i++){
		f[i][0]=1;
		for(int j=1;j<=i;j++)
			f[i][j]=f[i-1][j]+f[i-1][j-1];
	}
}

inline int calc(int x,int y){
	int cnt=0,ans=0;
	for(int i=31;i>=1;i--){
		if((x>>i)&1){
			cnt++;
			if(cnt>y) break;
			x=x^(1<<i);
		}
		if((1<<(i-1))<=x)
			ans+=f[i-1][y-cnt];
	}
	if(cnt+x==y) ans++;
	return ans;
}

inline int solve(void){
	int len=0;
	for(int i=0,tmp;i<=31;i++){
		tmp=calc(m,i)-calc(n-1,i);
		if(tmp>=k) break;
		k-=tmp;len=i+1;
	}
	long long l=n,r=m,ans;
	while(l<=r){
		long long mid=(l+r)>>1;
		if(calc(mid,len)-calc(n-1,len)>=k)
			ans=mid,r=mid-1;
		else
			l=mid+1;
	}
	return ans;
}

signed main(void){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	scanf("%d",&cas);init();
	while(cas--){
		scanf("%d%d%d",&n,&m,&k);
		if(m==0&&n==0){
			puts("0");
			continue;
		}
		int flag=0;
		if(n==0) n++,k--;
		if(m==0) m--,k--;
		if(n<0) n^=(1<<31),flag=1;
		if(m<0) m^=(1<<31),flag=1;
		ans=solve();
		if(flag) printf("%d\n",(ans^(1<<31)));
		else printf("%d\n",ans);
	}
	return 0;
}

  


By NeighThorn

posted @ 2017-04-06 20:12  NeighThorn  阅读(262)  评论(0编辑  收藏  举报