传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2728
思路:这是不错的题目,做了好久,原因是想错一个地方。
我们首先发现这个长得很像NERD的NAND其实是类似于基本位运算的一种运算法则,我们不禁想要用熟悉的基本位运算表示它,其实观察得到(题目名字是提示):
a NAND b = !a or !b = !( a & b)
so:
a NAND a = !a
a & b = !(a NAND b)
a or b = !a NAND !b
把三种基本运算都联系起来了,实际上NAND功能极为强大,能做到三种运算,而且实际上是一一对应的,于是我们将问题转化为用三种基本的位运算能表示多少
[L,R]
的数字。
然而我们发现运算过程中使用
!
其实可以转化为初始的A数组取!然后加入运算,这时运算只剩下
a or b = (a & b) or (a - a & b) or (b - a & b) ,我们发现,我们的or运算也几乎没有用啦!我们可以通过&运算分解为一些较小的基底(类似于线性基),最后一步再将基底
or
起来就好,于是我们第一步将
!
去掉,最后一步把
复杂度:
O(N∗K+K2)
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define N 1000
#define C 60
using namespace std;
typedef long long LL;
LL n,k,l,r,a[(N << 1) + 5],f[C + 5],g[C + 5],ans;
bool vis[C + 5];
inline LL getnum(){
char c;LL num,flag = 1;
while (!isdigit(c = getchar()))
if (c == '-') flag = -1;
num = c - '0';
while (isdigit(c = getchar())) num = 10 * num + c - '0';
return num * flag;
}
inline void init(){
memset(a,0,sizeof(a));
n = getnum(); k = getnum(); l = getnum(); r = getnum();
for (LL i = 1;i <= n; ++i) a[i] = getnum();
for (LL i = 1;i <= n; ++i)
for (LL j = 0;j < k; ++j)
if (!(a[i] & (1LL << j))) a[i + n] += (1LL << j);
}
inline void DO_IT(){
LL sum;
memset(f,-1,sizeof(f));
memset(g,0,sizeof(g));
for (LL i = k - 1;i >= 0; --i)
if (f[i] < 0)
{
sum = (1LL << k) - 1;
for (LL j = 1;j <= (n << 1); ++j)
if (a[j] & (1LL << i)) sum &= a[j];
for (LL j = k - 1;j >= 0; --j)
if (sum & (1LL << j)) f[j] = i;
g[i + 1] = 1;
}
for (LL i = 1;i <= k; ++i) g[i] += g[i - 1];
}
inline LL query(LL x){
if (x == -1) return 0;
LL num = 0,sum,p = 0;
ans = (1 << g[k]);
for (LL i = C - 1;i >= 0; --i){
if (i == 9)
++p;
if (!((1LL << i) & x))
{
bool flag = 1;
memset(vis,0,sizeof(vis));
for (LL j = C - 1;j > i; --j)
if (x & (1LL << j))
if (f[j] < 0) { flag = 0; break; }
else vis[f[j]] = 1;
if (f[i] < 0) flag = 0;
else vis[f[i]] = 1;
if (flag){
sum = 0;
for (LL j = C - 1;j >= i; --j)
if (f[j] >= 0)
if (vis[f[j]]) ++sum;
if (sum == num + 1) ans -= (1LL << g[i]); }
}
else ++num;
}
return ans;
}
int main(){
init();
DO_IT();
cout<<query(r) - query(l - 1);
return 0;
}
注意:1.当遇到大数还涉及循环变量的运算要开LL
2.运算例如(1 << 33)是错误的,一定要记得在数字后加LL
如:(1LL << 33LL)