题解 P5141 【列队】
题解放这里吧!
当然也在my blog里放了一份
吐槽ing:一道有趣的二进制题.
注意加粗部分是限制条件
我们先考虑暴力分
首先那个
对于这个暴力分,应该是一种很暴力(废话)的解法,我们直接从
考虑
这里我们就要开始讨论二进制算法了。
首先我们考虑巨佬站队方式的限制,对于每个询问
我们再考虑巨佬的站队对蒟蒻的约束作用(即限制了某些位必须是0):
首先我们假设没有这个条件,很显然的,如果第
接着我们再考虑存在某些位必须是
这里的方法是把所有有约束条件的
比如这里有一个第
1 0 1 0 1 0
我们把所有被限制的0删去,得到一个没有被限制的数:
1 1 1
显然这个
1 1 0
然后,我们再把刚才去掉的0加回去
1 0 1 0 0 0
这就是我们要找的数。
关于删除操作,我是这样做的,假设我们要删除下面这个二进制的第3个数:
1 1 1 1 1 1
^
我们先把后2位取下来:
1 1 1 1 0 0
s = 1 1
再删除第3位:
1 1 1 0 0 0
整体右移一位,在把
插入也是同理(改成左移)
具体代码实现(时间复杂度
void del(LL c)
{
LL o = c - 1;
s = ((((s & (~ c)) & (~ o)) >> 1) ^ (s & o));
}
void insert(LL c)
{
LL o = c - 1;
x = (((x & (~ o)) << 1) ^ (x & o));
}
然后还有一个问题就是,我们首先要找到满足
我们同样先不考虑后面的限制,我们把
对于其他情况,不难发现,我们必须要取
最后,找到了
代码也不长(甚至连数组都不用开),但是如果没思路的话,代码不一定看得懂:
#include<cstdio>
#include<iostream>
#define LL unsigned long long
using namespace std;
LL n,m,t,q;
LL p,a,b,k,s,x;
void del(LL c)
{
LL o = c - 1;
s = ((((s & (~ c)) & (~ o)) >> 1ll) ^ (s & o));
}
void getmax()
{
LL j = (1ll << (n - 1));
for( ;j ; (j >>= 1))
if((b & j) && (p & j)) break;
if(!j) k = b;
else k = ((b & (~ j)) | (j - 1));
j = (1ll << (n - 1));
for(; j; (j >>= 1))
if(p & j) k = (k & (~ j));
j = (1ll << (n - 1));s = k;
for(; j; (j >>= 1))
if((k & (~ (j - 1))) && (p & j)) del(j);
}
void insert(LL c)
{
LL o = c - 1;
x = (((x & (~ o)) << 1ll) ^ (x & o));
}
int main()
{
scanf("%lld%lld", &n,&m);
for(LL i = 0;i<n;++i)
{
scanf("%lld", &t);
p |= (t << i);
}
while(m --)
{
scanf("%lld%lld%lld",&a,&b,&q);
b = min(p-1, b);
getmax();
x = s - q + 1;
for(LL j = 1;j <= (1ll << n);j <<= 1)
if(p & j) insert(j);
if(x < a|| x>p) printf("POOR AFO!\n");
else printf("%lld\n", x % 20031102);
}
}