题意
给定 m m m 个字符串,每个字符串仅含 0/1/? \texttt{0/1/?} 0/1/?,长度均为 n n n。 0 \texttt{0} 0 和 1 \texttt{1} 1 分别可以和自己匹配, ? \texttt{?} ? 可以匹配 0 \texttt{0} 0 和 1 \texttt{1} 1。
定义一个 0/1 \texttt{0/1} 0/1 串可以匹配一个仅含 0/1/? \texttt{0/1/?} 0/1/? 的串当且仅当它们长度相同且对应位置均可匹配。
有 q q q 次询问,每次进行一种操作:
- 修改第 i i i 个字符串为另一个仅含 0/1/? \texttt{0/1/?} 0/1/? 的长度为 n n n 的串;
- 给定 [ l , r ] [l,r] [l,r],查询有多少 0/1 \texttt{0/1} 0/1 串可以匹配到第 l l l 个到第 r r r 个串全体。
思路
考虑对于一段所有字符串的第 x x x 个字符,一共有四种可能:确定为 0 \texttt{0} 0;确定为 1 \texttt{1} 1;都可以;都不可以。如果 0/1 \texttt{0/1} 0/1 都不可以则答案为 0 0 0,因为不会有任何一个字符串匹配该区间。如果确定为某个数,则这一位只有一种可能;否则这一位有两种可能。根据乘法原理,如果有 a a a 个位置有两种可能,则本次询问的答案为 2 a 2^a 2a。
注意到字符串长度只有
30
30
30,所以一个串每个位置的信息可以用一个 pair<int,int>
存下来:
first
的第 i i i 位为 0 0 0 表示第 i i i 个字符可以是 0 \texttt0 0 也可以是 1 \texttt1 1;first
的第 i i i 位为 1 1 1 表示第 i i i 个字符是second
的第 i i i 位。
这个信息可以推广到区间上:
[
l
,
r
]
[l, r]
[l,r] 内每个位置的信息也可以用这样一个 pair<pair<int, int>, bool>
存下来:
first.first
的第 i i i 位为 0 0 0 表示这个区间里所有的串第 i i i 个字符可以是 0 \texttt0 0 也
可以是 1 \texttt1 1;first.second
的第 i i i 位为 1 1 1 表示这个区间里所有串的第 i i i 个字符是second
的第 i i i 位。
second
为
0
0
0 表示这个区间的信息不合法。转移时从左右孩子进行一下位运算合并。如果两个孩子已确定的
位置有冲突则区间不合法。具体的实现见代码。
Code
// 这道题是个区间询问,单点修改,所以有兴趣的读者可以去看看树状数组的题解(本人蒟蒻不会
#include<bits/stdc++.h>
#define lson l,mid,rt << 1
#define rson mid + 1,r,rt << 1 | 1
#define mk(x,y,z) make_pair(make_pair(x,y),z)
#define type pair<pair<int,int>,bool>
using namespace std;
const int maxn = 1e5 + 5;
type sg[maxn << 4]; int x[maxn],y[maxn]; // sg为线段树,x,y分别存储原串的?位 & 原串的0/1位
char s[maxn],k[maxn]; int nowl,nowr,n,m,q; // nowl和nowr表示查询区间为[nowl,nowr]
void update(int rt) {
int ls = rt << 1, rs = rt << 1 | 1;
sg[rt].first.first = sg[ls].first.first | sg[rs].first.first,
// 当前区间的自由位,为左右子区间同时为自由位的位
sg[rt].first.second = sg[ls].first.second | sg[rs].first.second,
// 当前区间的固定位,为左右子区间同一位上至少有一个固定位的位
sg[rt].second = sg[ls].second & sg[rs].second & !((sg[ls].first.first & sg[rs].first.first) & (sg[ls].first.second ^ sg[rs].first.second));
// 答案为0的情况:
// 1. 左右孩子本身答案就为0
// 2. 左右孩子在固定位上产生冲突
}
void build(int l,int r,int rt) {
if (l == r) {
sg[rt] = mk(x[l],y[l],true); // 自己总是可以匹配自己
return ;
}
int mid = (l + r) >> 1;
build(lson); build(rson); update(rt);
// cout << sg[rt]
}
type query(int l,int r,int rt) {
if (nowl <= l && r <= nowr) return sg[rt];
int mid = (l + r) >> 1;
if (nowl <= mid && mid < nowr) {
type ls = query(lson), rs = query(rson), res;
res.first.first = ls.first.first | rs.first.first,
res.first.second = ls.first.second | rs.first.second,
res.second = ls.second & rs.second & !((ls.first.first & rs.first.first) & (ls.first.second ^ rs.first.second));
// 返回值与update计算原理相同
return res;
}
if (nowl <= mid) return query(lson);
if (mid < nowr) return query(rson);
}
void modify(int l,int r,int rt,int u,int v) { // 用nowl记录修改位置
if (l == r) {
sg[rt] = mk(u,v,true);
return ;
}
int mid = (l + r) >> 1;
if (nowl <= mid) modify(lson,u,v);
else modify(rson,u,v);
update(rt);
}
int main() {
scanf("%d%d%d",&n,&m,&q);
for (int i = 1;i <= m;i ++) {
scanf("%s",s); // 当第i位为自由位的时候我们令第i位固定位为0,方便处理
for (int j = 0;j < n;j ++)
if (s[j] != '?') // 固定位
x[i] |= 1 << (n - j),
y[i] |= (s[j] - '0') << (n - j);
}
build(1,m,1); int ans = 0;
for (int t = 1,op,u,v,now;t <= q;t ++) {
scanf("%d",&op);
if (op) { // 题意中的操作一
scanf("%d %s",&nowl,k), u = v = 0;
for (int j = 0;j < n;j ++) // 按照最开始的方法计算出修改值串的信息
if (k[j] != '?')
u |= 1 << (n - j),
v |= (k[j] - '0') << (n - j);
modify(1,m,1,u,v);
} else { // 提议中的操作二
scanf("%d%d",&nowl,&nowr), now = 1;
type res = query(1,m,1);
if (!res.second) { // 答案为0
ans ^= 0;
continue;
}
for (int i = 1;i <= n;i ++) // 按照乘法原理计算个数
if (!(res.first.first & (1 << (i))))
now <<= 1;
ans ^= now;
}
}
printf("%d",ans);
return 0;
}
- 评测信息:R127354197 记录详情