首先看到题目要求实现区间询问和单点修改,又发现题目有一条特殊性质——(nleq30)。由此可以想到对 (m) 建一棵线段树,每个节点维护字符串压缩后的信息。
接下来思考如何状压这个字符串。字符串中有三种字符 0
,1
,?
。可以将 0
状压为 ((10)_2),1
状压为 ((01)_2),?
状压为 ((11)_2)。每次问一个区间内字符串的方案数就相当于将所有点进行 &
运算,这样可以保证 ?
在碰到 0
或 1
的时候可以直接变成对应数字,而当一个 0
和 1
做 &
运算的时候对应值会变为 0
,在查询的时候方便特判无解的情况。
以上操作对修改同理。
一些小细节:线段树走到空区间的时候传一个 unsigned long long
型的二进制位均为 1
的整数即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define Int unsigned long long
template<typename _T>
inline void read(_T &x)
{
x=0;char s=getchar();int f=1;
while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
x*=f;
}
#define lowbit(x) (x&(-x))
#define gb(x) ((x-1)/T + 1)
#define gl(x) ((x-1)*T + 1)
#define pb push_back
#define fi first
#define sd second
#define Re register
#define MOD(x) (x = (x + mod)%mod)
#define pii pair<int,int>
const int np = 1e5 + 15;
char c[np][40];
unsigned int INF;
int a[np];
char s[np];
struct node{
int l,r;
int state;
node *ls,*rs;
inline bool inrange(int L,int R){return L <= l && r <= R;}
inline bool outofrange(int L,int R){return r<L||R<l;}
inline void pushup()
{
state = ls->state & rs->state ;
}
inline void upd(int L,int R,int vl)
{
if(inrange(L,R))
{
state = vl;
return;
}
else
{
if(!outofrange(L,R))
{
ls->upd(L,R,vl);
rs->upd(L,R,vl);
pushup();
}
else return;
}
}
inline int query(int L,int R)
{
if(inrange(L,R))
{
return state;
}
else
{
if(!outofrange(L,R))
{
return ls->query(L,R) & rs->query(L,R);
}
else return INF;
}
}
}mem[np * 2],*pool = mem,*rot;
inline node *New(){return ++pool;}
inline node *build(int L,int R)
{
node *u = New();
u->l = L, u->r = R;
if(L == R)
{
u->ls = u->rs = NULL;
u->state = a[L];
}
else
{
int mid = L + R >> 1;
u->ls = build(L,mid);
u->rs = build(mid + 1,R);
u->pushup();
}
return u;
}
inline int solve(char *g,int len)
{
int state = 0;
for(int i=1;i<=len;i++)
{
if(g[i] == '1')
{
state += 1ll<<(2*(i-1));
}
if(g[i] == '0')
{
state += 2ll<<(2*(i-1));
}
if(g[i] == '?')
{
state += 3ll<<(2*(i-1));
}
}
return state;
}
signed main()
{
INF = 0;
INF--;
int n,m,q;
read(n);
read(m);
read(q);
for(int i=1;i<=m;i++)
{
scanf("%s",c[i] + 1);
int op = strlen(c[i] + 1);
a[i] = solve(c[i],op);
}
rot = build(1,m);
int Ans = 0;
for(int i=1,opt,l,r,pos;i<=q;i++)
{
read(opt);
switch(opt)
{
case 0:{
read(l);
read(r);
int k = rot->query(l,r);
int g = 3,ans = 0;
for(int i=0;i<2 * n;i+=2)
{
if((g<<i&k) == g<<i) ans++;
if(!((k>>i)&3))
{
ans = -1;
break;
}
}
if(ans == -1) Ans ^= 0;
else Ans ^= (1ll<<ans);
break;
}
case 1:{
read(pos);
scanf("%s",s+1);
int p_ = solve(s,strlen(s + 1));
rot->upd(pos,pos,p_);
break;
}
}
}
printf("%lld
",Ans);
}
End
2020 年暑假 @一扶苏一 给我们讲了线段树,这种指针形式实现的线段树就是当年的成果。这道题就是当时的作业题,当时机房里只有 cyc 做了出来。当时扶苏和 cyc 一块给我们讲了很久,但是仍旧不明白如何实现。当一年之后,我已经熟练掌握了状压和线段树,再看到这个题,心里只想着一句话「这题似乎不是特别难」,即使这样,这题在我心里也有着不同于其它题目的分量。
感谢扶苏学长。撒花~