BZOJ - 2728 与非

题意:

  给定N个数,一个数k和一个范围[L,R]。每个数可以使用任意次,k表示与非不超过k位。求出范围内有多少个数可以由他们的与非和表示。

题解:

  m个数进行NAND,最终的数二进制下某一位如果为1,那么这m个数二进制下这一位也肯定为1。所以如果N个数某些位相同的话就可以把这些位并成一个基。

  因为aNANDa = !a,所以某一位如果为0可以转化为1。

  然后用预处理的基从高位开始贪心的选择。如果当前位取0,则情况数为(1<<cnt-i)-1(不包含0);当前位取1时情况数为1。一共是(1<<cnt-i)。

  题目中L的范围给错了,可能为0。所以当L为0时应该+1即-(-1)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
int n, k, cnt;
ll l, r, noww;
int vis[N];
ll a[N], w[70];
ll digit(ll x) {
    if(x==-1) return -1;
    ll res = 0, val = 0;
    for(int i = 1; i <= cnt; i++) {
        if(val+w[i] <= x) {
            val += w[i];
            res += (1ll<<cnt-i);
        }
    }
    return res;
}
int main() {
    scanf("%d%d%lld%lld", &n, &k, &l, &r);
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    noww = (1ll<<k)-1;
    for(int i = k-1; i >= 0; i--) {
        if(vis[i]) continue;
        ll ed = noww;
        for(int j = 1; j <= n; j++) 
        if(a[j]&(1ll<<i)) ed &= a[j];
        else ed &= (~a[j]);
        for(int j = 0; j < i; j++) if(ed&(1ll<<j)) vis[j] = 1;
        w[++cnt] = ed;
    }
    printf("%lld", digit(r) - digit(l-1));
}
View Code

 

posted @ 2018-05-25 18:41  Pneuis  阅读(150)  评论(0编辑  收藏  举报