• BZOJ 2728: [HNOI2012]与非(位运算)


    题意

    定义 NAND(与非)运算,其运算结果为真当且仅当两个输入的布尔值不全为真,也就是 A NAND B = NOT(A AND B) ,运算位数不会超过 (k) 位,

    给你 (n) 个整数 (A_i) ,这些数能任意进行无数次与非运算,最后问能运算出多少个在 ([L, R]) 区间的数。

    (k le 60, n le 1000, A_i < 2^k, 0 le L le R le 10^{18})

    题解

    参考了 kczno1 孔爷的题解。

    这个运算初看不太优美,其实我们可以利用它的一些性质。

    由于 A NAND A = NOT (A AND A) = NOT A 所以我们就可以得到了 NOT (非) 运算。

    进一步我们利用 NOTNOT(A NAND B) = NOT(NOT(A AND B)) = A AND B ,就可以得到了 AND (与)运算。

    这样这个运算就变得十分优秀了,我们就转化成进行 NOTAND 然后我们就可以得到 所有二进制操作 了。

    然后利用这个性质,我们就可以得到一个更加有用的性质。

    对于第 (i) 位和第 (j) 位,如果所有 (A_k) 的第 (i) 位和第 (j) 位相同,那么最后的结果对于 (i, j) 这两位一定是一样的。

    否则这两位的取值互不影响,可以任意取都能构造出一组合法方案。

    我们就能把 (k) 位数划分成许多个等价类,每个等价类里面的元素取值都必须一样。

    然后为了算 ([l, r]) 区间的答案,我们令 (Calc(r))(le r) 能凑出来的数。

    那么答案为 (Calc(r) - Calc(l - 1)) 。至于如何算 (Calc(x)) 呢?我们按位考虑就行了。

    1. 具体的,如果枚举的位为 (0) ,那么忽略。

    2. 如果为 (1) ,假设这一位不能选,那么接下来以任意选而不会超也不会重复,所以方案数加上 (2^{sum}) ( (sum) 为接下来的集合的个数) 然后退出。

      如果能选。要么不选,那么 (2^{sum-1}) ;要么将集合中的数全部选了,再接着枚举后面。、

    复杂度是 (O(nk)) 的,不知道为什么 (n) 只开 (1000) 。。。石乐志。

    代码

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    
    using namespace std;
    
    typedef long long ll;
    
    template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
    
    template<typename T>
    inline T read() {
        T x(0), sgn(1); char ch(getchar());
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * sgn;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("2728.in", "r", stdin);
    	freopen ("2728.out", "w", stdout);
    #endif
    }
    
    typedef long long ll;
    
    const int N = 1e3 + 1e2, K = 60;
    
    ll a[N], sta[N], now, full;
    
    int n, k, sum[K];
    
    inline ll Calc(ll x) {
    	if (x >= full) return 1ll << sum[k - 1];
    	ll res = 0;
    	for (int i = k - 1; x >= 0 && i >= 0; -- i) 
    		if (x >> i & 1) {
    			if (sta[i]) {
    				res += 1ll << (sum[i] - 1); x -= sta[i];
    			}
    			else {
    				res += 1ll << sum[i]; break;
    			}
    		}
    	return res + (x == 0);
    }
    
    int main () {
    
    	File();
    
    	n = read<int>(); 
    	full = (1ll << (k = read<int>())) - 1;
    
    	ll l = read<ll>(), r = read<ll>();
    	For (i, 1, n) a[i] = read<ll>();
    
    	ll have = 0;
    	Fordown (i, k - 1, 0) if (!(have >> i & 1)) {
    		ll now = full;
    		For (j, 1, n)
    			now &= (a[j] >> i & 1) ? a[j] : ~a[j];
    		have |= (sta[i] = now); sum[i] = 1;
    	}
    	For (i, 1, k - 1) sum[i] += sum[i - 1];
    
    	printf ("%lld
    ", Calc(r) - Calc(l - 1));
    
    	return 0;
    
    }
    
  • 相关阅读:
    myelipse与idea的javaweb项目创建
    入站规则和出站规则设置
    NAT技术基本原理与应用
    如何将sqlserver数据中的数据导出到服务器
    如何将SqlServer中表结构以及表数据全部导出
    国外服务器--新加坡服务器
    windows server2008 创建新用户 远程桌面连接 和 多用户登录问题
    程序员应该关注的国外IT网站
    IDEA创建普通java和web项目教程
    IIS Express 通过IP访问的方法和坑
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/10096371.html
Copyright © 2020-2023  润新知