Codeforces #698 (Div. 2) E. Nezzar and Binary String 题解

中文题意:

\(T\) 组数据c++

给你两个长度为 \(n\) 的01串 \(s,f,\)\(q\) 次询问。ui

每次询问有区间 \([\ l,r\ ]\) ,若是 \([\ l,r\ ]\) 同时包含\(0\)\(1\),则询问终止,不然你能够改变区间\([\ l,r\ ]\) 内严格小于 \(len_{lr}\) 的数字。spa

问是否能够使得询问不终止,且通过 \(q\) 次询问后能够将\(s\)改成\(f\)code

前置知识:

线段树

没了字符串

思路:

发现无法正序推过去(反正我不会),考虑根据询问逆推。get

那么对于 \(f\) ,和 \(q_{1},q_{2}\)···\(q_{n}\) ,用 \(l_{i},r_{i}\) 来表示 \(q_{i}\) , \(s_{i}\)表示通过前 \(i\) 次询问后的字符串 \(s\)it

对于第 \(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}\) 时,class

\(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;
}
相关文章
相关标签/搜索