题解 CF1477B 【Nezzar and Binary String】
fanfansann · · 题解
E - Nezzar and Binary String
A 有一个长度为
与此同时,A还有一个任务,就是他希望可以通过这
4
5 2
00000
00111
1 5
1 3
2 1
00
01
1 2
10 6
1111111111
0110001110
1 10
5 9
7 10
1 7
3 5
6 10
5 2
10000
11000
2 5
1 3
YES
NO
YES
NO
Solution
我们本来按照题意的顺序,是她先查看,我们再修改,但是每次改都要受到之后她查看的区间的影响。
比如样例一:
第一天,她要看
[1,5] ,很开心走了 ~第一天晚上,我可以改
[1,5] ,但是她明天要看[1,3] ,所以我要先保证[1,3] 是没有0,1 同时存在的,再看能不能改[1,5] 的其他元素使得s\to f ,就很怪,当前的操作需要考虑之后的影响,我们还需要提前知道下一次的操作区间,就很奇怪。第二天,她要看
[1,3] ,因为前一天晚上我的努力,她很开心的走了 ~第二天晚上,我可以改
[1,3] ,明天她不看了,我就尽量改[1,3]$ 使得$s\to f
但是如果我们反过来,变成我们先改,她再看,也就是我们早上先改,晚上她再看,就很舒服。
第一天早上,我可以改
[1,3] ,因为她晚上要看[1,3] ,所以我们只需要保证[1,3] 是没有0,1 同时存在的,再尽量朝着f\to s 的方向改[1,3] 。第一天晚上,她看完
[1,3] ,开心地走了 ~第二天早上,我可以改
[1,5] ,因为她晚上要看[1,5] ,我们就保证[1,5] 是没有0,1 同时存在的,再尽量朝着f\to s 的方向改[1,5] ,因为这是最后一天了,我发现可以直接把f 变成s ,并且s 串全是0 ,她看了一定很开心 (●ˇ∀ˇ●) ,我就这么做了第二天,她要看
[1,5] ,因为早上我的努力,她开心地蹦蹦跳跳的走了 ~
其实正序做不只是很做起来,而且最关键的是,我们需要自己考虑最优解,这是非常困难的,而我们交换顺序对题目的答案不会造成任何影响,但是却会给我们的实现带来很大的方便,并且稍微分析一下就会发现,逆序做,不需要我们有任何操作空间。
使用逆向思维,可以帮助我们简化这个问题。然后我们来考虑怎么解决这个问题。
我们将会从
由于我们只有一个操作:修改定区间内的二进制串,并且每次最多只能修改个数小于区间长度的一半。
实际上这就代表着我们只能有一种修改方式:将
但是如果区间内 NO 即可。
然后我们发现我们会跟着这个区间的设定,一步一步没有任何操作空间地从头修改到最后,所以如果最后我们得到的字符串,如果就是 YES 即可。
同理,如果最后得到的不是 NO 。
最后一个问题,如果快速地实现区间修改呢?
我们发现我们只需要有两个操作即可:区间全部修改(区间染色),区间查询(查询
而数据很大:
我们只需要维护区间和就行了,因为区间和就是1的个数。
由于需要区间查询,所以我们需要加上lazytag,就是我们修改的时候,区间是 -1 就好。
然后就是很简单的代码了:
Code
注意:毒瘤出题人把数据开到了
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef int itn;
const int N = 3e5 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;
int n, m, q;
int L[N], R[N];
char s[N], f[N];
struct Tree
{
int l, r;
int sum;//维护 1 的个数, 因为 0 求和还是 0 和就是 1 的个数
int lz;//1 / 0
}tr[N << 2];
void push_up(int p)
{
tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
}
void push_down(int p)
{
if(tr[p].lz == -1) return ;
tr[p << 1].lz = tr[p].lz;
tr[p << 1 | 1].lz = tr[p].lz;
tr[p << 1].sum = (tr[p << 1].r - tr[p << 1].l + 1) * tr[p].lz;//都是等于而不是加
tr[p << 1 | 1].sum = (tr[p << 1 | 1].r - tr[p << 1 | 1].l + 1) * tr[p].lz;
tr[p].lz = -1;
}
void build(int p, int l, int r)//f
{
tr[p].l = l, tr[p].r = r, tr[p].lz = -1;
if(l == r) {
tr[p].sum = f[l] - '0';
tr[p].lz = -1;
return ;
}
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
push_up(p);
}
void modify(int p, int l, int r, int val)
{
if(tr[p].l >= l && tr[p].r <= r) {
tr[p].sum = (tr[p].r - tr[p].l + 1) * val;//这里全部变成 1 ,sum 等于区间长度,全部变成 0, sum 就是 0
tr[p].lz = val;
return ;
}
push_down(p);
int mid = tr[p].l + tr[p].r >> 1;
if(l <= mid) modify(p << 1, l, r, val);
if(r > mid) modify(p << 1 | 1, l, r, val);
push_up(p);
}
int query(int p, int l, int r)
{
if(tr[p].l >= l && tr[p].r <= r) {
return tr[p].sum;
}
push_down(p);
int mid = tr[p].l + tr[p].r >> 1;
int res = 0;
if(l <= mid) res += query(p << 1, l, r);
if(r > mid) res += query(p << 1 | 1, l, r);
return res;
}
bool check()
{
for(int i = 1; i <= n; ++ i) {
if(query(1, i, i) != s[i] - '0') {
return false;
}
}
return true;
}
bool solve()
{
scanf("%d %d", &n, &q);
scanf("%s %s", s + 1, f + 1);
for (int i = 1; i <= q; ++ i) {
scanf("%d %d", &L[i], &R[i]);
}
build (1, 1, n);
for(int i = q; i >= 1; -- i) {
int l = L[i], r = R[i];
int num1 = query(1, l, r);//sum
int len = r - l + 1;
if (num1 * 2 == len) return false;// 0 和 1 的长度相等
else if(num1 * 2 < len) modify(1, l, r, 0);
else modify(1, l, r, 1);
}
if (!check()) {
return false;
}
return true;
}
itn t;
int main()
{
scanf("%d", &t);
while (t -- ) {
if (solve()) {
puts("YES");
}
else {
puts("NO");
}
}
return 0;
}