動態規劃----SPOJ 1182. Sorted bit squence (數位統計+二分)

//
// Created by liyuanshuo on 2017/3/22.
//


/*
 * 參考:https://wenku.baidu.com/view/d2414ffe04a1b0717fd5dda8.html
 *
 * 參考:http://blog.csdn.net/acm_cxlove/article/details/7859816
 *
 * 題目鏈接:http://www.spoj.pl/problems/SORTBIT/
 *
 * 題目大意:
 * 將區間[m,n]內的所有整數按照其二進制表示中1的數量從小到大排序。如果1的數量
 * 相同,則按照數的大小排序。求這個序列中的第k個數。其中,負數使用補碼來表示:一個
 * 負數的二進制表示與其相反數的二進制之和恰好等於2^32。
 *
 * 例如,當m=0,n=5 時,排序後的序列如下:
 * 編號 	十進制數 		二進制表示
 * 1 	0 			0000 0000 0000 0000 0000 0000 0000 0000
 * 2 	1 			0000 0000 0000 0000 0000 0000 0000 0001
 * 3 	2 			0000 0000 0000 0000 0000 0000 0000 0010
 * 4 	4 			0000 0000 0000 0000 0000 0000 0000 0100
 * 5 	3 			0000 0000 0000 0000 0000 0000 0000 0011
 * 6 	5 			0000 0000 0000 0000 0000 0000 0000 0101
 *
 * 當m=-5,n=-2 時,排序後的序列如下:
 *
 *	編號   		十進制數         			二進制表示
 *	1            -4             1111 1111 1111 1111 1111 1111 1111 1100
 *	2            -5             1111 1111 1111 1111 1111 1111 1111 1011
 *	3            -3             1111 1111 1111 1111 1111 1111 1111 1101
 *	4            -2             1111 1111 1111 1111 1111 1111 1111 1110
 *
 
 * 輸入:包含多組測試數據。第一行是一個不超過 1000 的正整數,表示測試數據數量。
 * 每組數據包含 m,n,k 三個整數。
 * 輸出:對於每組數據,輸出排序後的序列中第 k 個數。
 * 數據規模:m × n ≥ 0, -231 ≤ m ≤ n ≤ 231-1  ,1 ≤ k ≤ min{n − m + 1, 2 147 473 547}。
 *
 * 分析:
 * 我們首先考慮 m、n 同正的情況。
 * 由於排序的第一關鍵字是 1 的數量,第二關鍵字是數的大小,因此我們很容易確定答案 中 1 的個數:依次統計區間[m,n]內二進制表示中含 1 的數量爲 0,1,2,…的數,直到累加的答 案超過
 * k,則當前值就是答案含 1 的個數,假設是 s。利用例一的算法可以解決這個問題。 同時,我們也求出了答案是第幾個[m,n]中含 s 個 1 的數。因此,只需二分答案,求出[m,ans] 中含 s 個
 * 1 的數的個數進行判斷即可。
 * 由於每次詢問的複雜度爲 O(log(n)),故二分的複雜度爲 O(log2(n)),這同時也是預處理
 * 的複雜度,因此此算法較爲理想。
 * m<0 的情況,也不難處理,我們只要忽略所有數的最高位,求出答案後再將最高位賦回 1 即可。或者也可以直接將負數視爲 32 位無符號數,採用同正數一樣的處理方法。兩種方 法都需要特別處理 n=0
 * 的情況。
 *
 */
#include <iostream>

using namespace std;

int f[35][35];

int count_number_k( int n, int k )
{
	int sum = 0, tot = 0;
	for (int i = 31; i ; --i)
	{
		if ( n & (1<<i) )
		{
			tot++;
			if ( tot > k )
				break;
			n ^= (1<<i);
		}
		if( (i<<(i-1)) <= n )
			sum += f[i-1][k-tot];
	}
	return sum;
}

int get_answers( int l, int r, int k)
{
	int len = 1;
	for (int i = 1; i <= 31 ; ++i)
	{
		int tmp = count_number_k (r,i) - count_number_k (l-1, i);
		if ( k <= tmp )
			break;
		k -= tmp;
		len = i+1;
	}
	int low = l, high = r, mid;
	while ( low < high )
	{
		mid = (int)(((long long)low + (long long)high) / 2);
		int tmp = count_number_k (mid, len) - count_number_k (l-1, len);
		if ( tmp < k )
		{
			low = mid + 1;
		} else
		{
			high = mid;
		}
	}
	return low;
}
int main_sorted_by_sequence( )
{
	int t, l, r, k;
	for (int i = 0; i <= 32 ; ++i)
	{
		f[i][0] = f[i][i] = 1;
		for (int j = 1; j <i ; ++j)
		{
			f[i][j] = f[i-1][j] + f[i-1][j-1];
		}
	}
	cin>>t;
	while ( t-- )
	{
		cin>>l>>r>>k;
		if( l==0 && r==0 )
		{
			cout<<"0"<<endl;
			continue;
		}
		if( l >= 0 && r >= 0 )
		{
			if( l == 0 )
			{
				k--;
				l = 1;
			}
			if( k == 0 )
			{
				cout<<"0"<<endl;
				continue;
			}
			cout<<get_answers (l, r, k)<<endl;
		}
		else
		{
			if ( r == 0 )
			{
				k--;
				r = -1;
			}
			l &= (~(1<<31));
			r &= (~(1<<31));
			cout<<((1<<31)|get_answers (l, r, k))<<endl;
		}
	}
	return 0;
}

点赞