[ABC341E] Alternating String 讲解
题目
考察:线段树,树状数组,set,差分。
方法一:
这道题与 P3870 有点相似,只不过它是区间求和,而这道题是判断是不是好字符串。
我们不难发现,好的字符串有以下特征:
- 好的字符串取反后必为好的字符串(不好的字符串取反后必为不好的字符串)。
- 好的字符串的子串必为好的字符串(不好的字符串的母串必为不好的字符串)。
然后我们来看个图:
所以说我们要存三个值:首端点值,尾端点值和判断变量,我们分别用
我们得到状态转移方程:
边界条件为(
修改时:
pushdown 函数:
易得代码(时间:
#include<bits/stdc++.h>
#define int long long
#define inl inline
#define INF 214748364721474836
using namespace std;
inl int read(){
int f=1,x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f*x;
}
inl void write(int x){
if(x<0){
x=-x;
putchar('-');
}
if(x>=10) write(x/10);
putchar(x%10^48);
}
const int N=5e5+5;
struct trees{
int l,r,begin,end;
bool vis,lan;
}t[N<<2];
string s;
int n,q,a[N],op,l,r;
inl void build(int x,int l,int r){
t[x].l=l;
t[x].r=r;
if(l==r){
t[x].vis=1;
t[x].begin=a[l];
t[x].end=a[r];
// cout<<x<<' '<<t[x].l<<' '<<t[x].r<<' '<<t[x].begin<<' '<<t[x].end<<' '<<t[x].vis<<endl;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
t[x].vis=(t[x<<1].vis&&t[x<<1|1].vis&&t[x<<1].end!=t[x<<1|1].begin);
t[x].begin=t[x<<1].begin;
t[x].end=t[x<<1|1].end;
// cout<<x<<' '<<t[x].l<<' '<<t[x].r<<' '<<t[x].begin<<' '<<t[x].end<<' '<<t[x].vis<<endl;
}
inl void pushdown(int x){
if(!t[x].lan) return;
t[x<<1].begin^=1;
t[x<<1].end^=1;
t[x<<1|1].begin^=1;
t[x<<1|1].end^=1;
t[x<<1].lan^=1;
t[x<<1|1].lan^=1;
// cout<<x<<' '<<t[x].l<<' '<<t[x].r<<' '<<t[x].begin<<' '<<t[x].end<<' '<<t[x].vis<<endl;
// cout<<(x<<1)<<' '<<t[x<<1].l<<' '<<t[x<<1].r<<' '<<t[x<<1].begin<<' '<<t[x<<1].end<<' '<<t[x<<1].vis<<endl;
// cout<<(x<<1|1)<<' '<<t[x<<1|1].l<<' '<<t[x<<1|1].r<<' '<<t[x<<1|1].begin<<' '<<t[x<<1|1].end<<' '<<t[x<<1|1].vis<<endl;
t[x].lan=0;
}
inl void add(int x,int l,int r){
if(l<=t[x].l&&t[x].r<=r){
t[x].lan^=1;
t[x].begin^=1;
t[x].end^=1;
// cout<<x<<' '<<t[x].l<<' '<<t[x].r<<' '<<t[x].begin<<' '<<t[x].end<<' '<<t[x].vis<<endl;
return;
}
pushdown(x);
int mid=(t[x].l+t[x].r)>>1;
if(l<=mid) add(x<<1,l,r);
if(mid<r) add(x<<1|1,l,r);
t[x].vis=(t[x<<1].vis&&t[x<<1|1].vis&&t[x<<1].end!=t[x<<1|1].begin);
t[x].begin=t[x<<1].begin;
t[x].end=t[x<<1|1].end;
// cout<<x<<' '<<t[x].l<<' '<<t[x].r<<' '<<t[x].begin<<' '<<t[x].end<<' '<<t[x].vis<<endl;
}
inl bool getans(int x,int l,int r){
if(l<=t[x].l&&t[x].r<=r) return t[x].vis;
pushdown(x);
int mid=(t[x].l+t[x].r)>>1;
bool vis=1;
if(l<=mid) vis&=getans(x<<1,l,r);
if(r>mid) vis&=getans(x<<1|1,l,r);
if(l<=mid&&r>mid) vis&=(t[x<<1].end!=t[x<<1|1].begin);
return vis;
}
signed main(){
n=read();
q=read();
cin>>s;
s=' '+s;
for(int i=1;i<=n;++i) a[i]=s[i]^48;
build(1,1,n);
for(int i=1;i<=q;++i){
op=read();
l=read();
r=read();
if(op==1) add(1,l,r);
else if(getans(1,l,r)) puts("Yes");
else puts("No");
}
return 0;
}
做法 2:
看到相邻两个数不同,我们便想到差分,样例中的数列差分后就是下面这个样子:
恍然大悟,不就是转化成了单点修改(修改左右两个端点),区间求和(若区间内的和正好等于长度,也就是每个元素都为
代码没什么好讲的,但需要注意两个点:
- 第
1 个点上的差分数组始终为1 。 - 第
l 个点上存的是第l-1 个点与它的差分,不能算入查询区间里。
代码(时间:
#include<bits/stdc++.h>
#define int long long
#define inl inline
#define INF 214748364721474836
using namespace std;
inl int read(){
int f=1,x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f*x;
}
inl void write(int x){
if(x<0){
x=-x;
putchar('-');
}
if(x>=10) write(x/10);
putchar(x%10^48);
}
const int N=5e5+5;
struct trees{
int l,r,sum;
}t[N<<2];
string s;
int n,q,a[N],op,l,r;
inl void build(int x,int l,int r){
t[x].l=l;
t[x].r=r;
if(l==r){
t[x].sum=a[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
}
inl void add(int x,int vis){
if(t[x].l==vis&&vis==t[x].r){
t[x].sum^=1;
return;
}
int mid=(t[x].l+t[x].r)>>1;
if(vis<=mid) add(x<<1,vis);
else add(x<<1|1,vis);
t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
}
inl int getans(int x,int l,int r){
if(l<=t[x].l&&t[x].r<=r) return t[x].sum;
int mid=(t[x].l+t[x].r)>>1,sum=0;
if(l<=mid) sum+=getans(x<<1,l,r);
if(r>mid) sum+=getans(x<<1|1,l,r);
return sum;
}
signed main(){
n=read();
q=read();
cin>>s;
s=' '+s;
a[1]=1;
for(int i=2;i<=n;++i) a[i]=s[i-1]^s[i];
build(1,1,n);
for(int i=1;i<=q;++i){
op=read();
l=read();
r=read();
if(op==1){
if(l!=1) add(1,l);
if(r!=n) add(1,r+1);
}else if(getans(1,l+1,r)==r-l) puts("Yes");
else puts("No");
}
return 0;
}
做法 3:
做法
要注意的还是上面那两个,不多说直接放代码(时间:
#include<bits/stdc++.h>
#define int long long
#define inl inline
#define INF 214748364721474836
using namespace std;
inl int read(){
int f=1,x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f*x;
}
inl void write(int x){
if(x<0){
x=-x;
putchar('-');
}
if(x>=10) write(x/10);
putchar(x%10^48);
}
#define T set<int>::iterator
string s;
int n,q,op,l,r;
set<int>st;
signed main(){
n=read();
q=read();
cin>>s;
s=' '+s;
for(int i=1;i<=n;++i)
if(s[i]==s[i-1]) st.insert(i);
for(int i=1;i<=q;++i){
op=read();
l=read();
r=read();
if(op==1){
if(st.find(l)==st.end()&&l!=1) st.insert(l);
else st.erase(l);
if(st.find(r+1)==st.end()) st.insert(r+1);
else st.erase(r+1);
}else{
T it=st.upper_bound(l);
if(it==st.end()||*it>r) puts("Yes");
else puts("No");
}
}
return 0;
}
(真的想吐槽一下专栏区)