zoukankan      html  css  js  c++  java
  • 【题解】P6218 [USACO06NOV] Round Numbers S

    题目传送门

    这是一道数位DP

    (dp_{i,j,k}) 为满足(i) 位组成,且其中有 (j) 个1,第 i 位(从右往左数)为 (k)二进制数的数量。

    可以得出状态转移方程:

    (dp_{i,j,0}=dp_{i-1,j,1}+dp_{i-1,j,0};(2le i,0le j< i))

    (dp_{i,j,1}=dp_{i-1,j-1,0}+dp_{i-1,j-1,1};(2le i,0<jle i))

    边界:(dp_{1,0,0}=1,dp_{1,1,1}=1)

    对于 (dp_{i,j,k}),如果满足 (1le i,0le jle lfloor frac{i}{2} floor),则这个状态是合法的。因为0的个数为 (i-j),要满足 (jle i-j),则 (2jle i) 所以 (jle lfloor frac{i}{2} floor)

    (f(x)) 为区间 ([1,x-1]) 内的“圆数”个数,则区间 ([L,R]) 内的“圆数”个数为 (f(R+1)-f(L))

    对于求(f(x)),我们先将 (x) 转换成二进制,设其二进制位数为 (len)

    1. 将二进制位数小于 (len) 的“圆数”个数统计到答案中。这时,对于 (dp_{i,j,k}),如果满足 (1le i,0le jle lfloor frac{i}{2} floor),则这个状态是合法的。因为0的个数为 (i-j),要满足 (jle i-j),则 (2jle i) 所以 (jle lfloor frac{i}{2} floor)

    2. 对于 (x) 的二进制除首位外的每一位 (i),都判断其是否为1 。如果为1,说明存在一些数,它们长度为 (len),值小于 (x),且二进制表示中的前 (i-1) 位与 (x) 相同,第 (i) 位为0 。然后将这些数中的“圆数”个数加入答案即可。这时,判断一个状态是否合法,需要考虑前 (i-1)位中0和1的个数。

    参考代码

    略微压行,轻喷。

    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    #define in __inline__
    typedef long long ll;
    #define rei register int
    #define FOR(i, l, r) for(rei i = l; i <= r; ++i)
    #define FOL(i, r, l) for(rei i = r; i >= l; --i)
    char inputbuf[1 << 23], *p1 = inputbuf, *p2 = inputbuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = inputbuf) + fread(inputbuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    in int read() {
    	int res = 0; char ch = getchar(); bool f = true;
    	for(; ch < '0' || ch > '9'; ch = getchar())
    		if(ch == '-') f = false;
    	for(; ch >= '0' && ch <= '9'; ch = getchar())
    		res = res * 10 + (ch ^ 48);
    	return f ? res : -res;
    }
    const int N = 40;
    
    ll dp[N][N][2];
    int a, b, A[N], la, lb, B[N];
    
    ll solve(int x[], int len) {
    	ll res = 0; int s0 = 0, s1 = 1;
    	//s0表示0的个数,s1表示1的个数
    	FOL(i, len - 1, 1) FOR(j, 0, (i >> 1)) res += dp[i][j][1];//第1类数
    	FOL(i, len - 1, 1) {//第二类数
    		if(x[i]) FOR(j, 0, i) if(s0 + i - j >= s1 + j) res += dp[i][j][0];
    		//s0+i-j表首位至当前位0的个数,s1+j表首位至当前位1的个数,注意j要枚举至i
    		x[i] ? (++s1) : (++s0);
    	}
    	return res;
    }
    
    signed main() {
    	a = read(), b = read();
    	for(; a; a >>= 1) A[++la] = a & 1;
    	for(; b; b >>= 1) B[++lb] = b & 1;//转换成二进制
    	++B[1];
    	for(rei i = 2; i <= lb && B[i - 1] == 2; ++i) B[i - 1] = 0, ++B[i];
    	if(B[lb] == 2) B[lb] = 0, B[++lb] = 1;
    	while(!A[la]) --la;
    	while(!B[lb]) --lb;//给B加上1
    	dp[1][0][0] = dp[1][1][1] = 1;
    	FOR(i, 2, lb) FOR(j, 0, i) {//DP
    		if(j < i) dp[i][j][0] = dp[i - 1][j][1] + dp[i - 1][j][0];
    		if(j) dp[i][j][1] = dp[i - 1][j - 1][0] + dp[i - 1][j - 1][1];
    	}
    	printf("%lld
    ", solve(B, lb) - solve(A, la));
    	return 0;//结束
    }
    
    
  • 相关阅读:
    C#操作Windows控制面板
    WPF打印控件内容
    LINQ函数
    通过实现System.IComparable接口的CompareTo方法对两个类进行比较
    泛型和约束
    CSS样式基础总结
    C#调用百度高精度IP定位API通过IP获取地址
    软件下载路径
    RNN学习资料
    mysql 不能插入中文记录
  • 原文地址:https://www.cnblogs.com/creating-2007/p/12941960.html
Copyright © 2011-2022 走看看