中文題意:
\(T\) 組資料
給你兩個長度為 \(n\) 的01串 \(s,f,\)有 \(q\) 次詢問。
每次詢問有區間 \([\ l,r\ ]\) ,如果 \([\ l,r\ ]\) 同時包含\(0\)和\(1\),則詢問終止,否則你可以改變區間\([\ l,r\ ]\) 內嚴格小於 \(len_{lr}\) 的數字。
問是否可以使得詢問不終止,且經過 \(q\) 次詢問後可以將\(s\)改為\(f\)。
前置知識:
線段樹
沒了
思路:
發現沒法正序推過去(反正我不會),考慮根據詢問逆推。
那麼對於 \(f\) ,和 \(q_{1},q_{2}\)···\(q_{n}\) ,用 \(l_{i},r_{i}\) 來表示 \(q_{i}\) , \(s_{i}\)表示經過前 \(i\) 次詢問後的字串 \(s\) 。
對於第 \(n\) 次詢問,當且僅當 \(s_{n-1}\)中的 \([l_{n},r_{n}]\) 全為 \(k\) ( \(k\) \(\in\) \((0,1)\) ) ,\(f\) 在 \([l_{n},r_{n}]\) 內(\(k\oplus 1\))的數量\(num_{k\oplus 1}\) \(<\) \(len_{lr}\) 時,
\(s_{n-1}\) 可轉化 \(f\) 。
因此,我們可以對於 \(f\) 從 \(n\) 開始向前遍歷詢問。對於 \([l_{i},r_{i}]\) , 將 \([l_{i},r_{i}]\) 內數量較少的數字改為另一個數字。
顯然,當 \([l_{i},r_{i}]\) 內 \(num_{1} = num_{0}\) 時,詢問會終止,因為改變數必須嚴格小於區間長度的一半。
遍歷到最後判斷 \(s\) 和經過轉化的 \(f\) 是否相同就行了。
做法:
對於區間,查詢和改變問題,我們可以用線段樹在 \(log\ n\) 的複雜度下解決。
首先對於 \(f\) 建立線段樹,維護區間內 \(1\) 的數量。
對於區間修改,建立 \(lazy\) 標記,\(-1\) 表示不變,\(0\) 表示 \(lazy\) 下的區間全為\(0\),\(1\) 表示 \(lazy\) 下的區間全為\(1\)。
\(pusdown\) 操作:
inline void pushdown(int p,int l,int r)
{
if(laz[p]==-1)//未被標記跳過
return ;
int mid=(l+r)>>1;
if(laz[p])//標記為1
{
tr[p<<1]=(mid-l+1);
tr[p<<1|1]=(r-mid);
laz[p<<1]=laz[p<<1|1]=1;
laz[p]=-1;
return ;
}
tr[p<<1]=tr[p<<1|1]=0;//標記為0
laz[p<<1]=laz[p<<1|1]=0;
laz[p]=-1;
}
剩下的就是線段樹的基本操作了。
Code:
#include<bits/stdc++.h>
#define N 240000
using namespace std;
int t,n,q;
char s[N],f[N];
int ql[N],qr[N],tr[N<<2],laz[N<<2];
inline int read()
{
char a=0;int w=1,x=0;
while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
return x*w;
}
inline void pushdown(int p,int l,int r)
{
if(laz[p]==-1)//未被標記跳過
return ;
int mid=(l+r)>>1;
if(laz[p])//標記為1
{
tr[p<<1]=(mid-l+1);
tr[p<<1|1]=(r-mid);
laz[p<<1]=laz[p<<1|1]=1;
laz[p]=-1;
return ;
}
tr[p<<1]=tr[p<<1|1]=0;//標記為0
laz[p<<1]=laz[p<<1|1]=0;
laz[p]=-1;
}
void build(int p,int l,int r)//建樹
{
laz[p]=-1;
if(l==r)
{
tr[p]=(f[l]^48);
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
int que(int p,int l,int r,int L,int R)//查詢1的數量
{
if(L<=l&&r<=R)
return tr[p];
pushdown(p,l,r);
int mid=(l+r)>>1;
int ans=0;
if(mid>=L)
ans+=que(p<<1,l,mid,L,R);
if(mid<R)
ans+=que(p<<1|1,mid+1,r,L,R);
return ans;
}
void modify(int p,int l,int r,int L,int R,int opt)//區間修改
{
if(L<=l&&r<=R)
{
tr[p]=opt*(r-l+1);
laz[p]=opt;
return ;
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(mid>=L)
modify(p<<1,l,mid,L,R,opt);
if(mid<R)
modify(p<<1|1,mid+1,r,L,R,opt);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
int main()
{
t=read();
while(t--)
{
n=read();
q=read();
int flag=1;
scanf("%s%s",(s+1),(f+1));
for(register int i=1;i<=q;i++)
{
ql[i]=read();
qr[i]=read();
}
build(1,1,n);
for(register int i=q;i>=1;i--)
{
int len=qr[i]-ql[i]+1;//區間長度
int num=que(1,1,n,ql[i],qr[i]);//查詢區間內1的數量
if( num==len-num )//區間內0的數量為 len-num , 0和1數量相同時不可能成立
{
flag=0;
break;
}
modify(1,1,n,ql[i],qr[i],num>(len-num) );//區間修改
}
if(!flag)
{
printf("NO\n");
continue;
}
for(register int i=1;i<=n;i++)
{
int num=que(1,1,n,i,i);//取出經過q次詢問後f的第i位
if(num!=(s[i]^48))//判斷f和s是否相等,不相等退出
{
flag=0;
break;
}
}
if(!flag)
{
printf("NO\n");
continue;
}
printf("YES\n");
}
return 0;
}