线段树+cf1478E

线段树+cf1478E

传送门

(先贴代码,细节以后补充QAQ)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int maxn = 2e5 + 10;//电脑竟然关机重启了,登录一小时,气死我了 
struct str {
  int l,r,sum,lazy;
};
struct fx{
  int x,y;
};
str tree[maxn<<2];
fx b[maxn];
int a[maxn],cs[maxn];
void pushup(int node){//线段树子节点与父亲节点关系的维护
  tree[node].sum=tree[node<<1|1].sum+tree[node<<1].sum;
}
void build(int node ,int start,int end){//建立线段树 
  tree[node]={start,end,0,-1};
  if(start==end){
    tree[node].sum=a[end];//边界条件,也就是到了叶子节点 
    return;
  } 
  int mid=(start+end)>>1;
  build(node<<1,start,mid);//左边的儿子的建立,用的是完全二叉树的性质
  build(node<<1|1,mid+1,end); //右边的儿子的建立。
  pushup(node); 
}
 
void pushdown(int node){
  if(tree[node].lazy!=-1){
    tree[node<<1].lazy=tree[node<<1|1].lazy=tree[node].lazy;//继承标记
    tree[node<<1|1].sum=(tree[node<<1|1].r-tree[node<<1|1].l+1)*tree[node].lazy,tree[node<<1].sum=(tree[node<<1].r-tree[node<<1].l+1)*tree[node].lazy;//传递数值,因为我们这里用的只是改变数值,所以不用去用加法,直接等于就行了 
    tree[node].lazy=-1;//父节点的标记去掉 
  }
  
}
void update(int node,int l, int r,int val)//start,end代表的是要用的区间(现在在查询的区间),l,r代表的是要改的区间,val是要修改的数值
{
  if(l<=tree[node].l&& tree[node].r<=r){//如果改变的区间在我们查询的区间之内就直接改变 
    tree[node].lazy=val;
    tree[node].sum=(tree[node].r-tree[node].l+1)*tree[node].lazy;
//		cout<<tree[node].sum<<"\n";
    return;
  }//如果不是正好在区间内,或者是有一半在里面啥的 
  pushdown(node);
//	cout<<"?\n";
  int m=(tree[node].r+tree[node].l)>>1;
  if(l<=m) update(node<<1,l,r,val);//如果要查询的区间在mid左边有交集,那么就去左边继续找 
  if(r>m) update(node<<1|1,l,r,val);//如果要查询的区间在mid右边。
  pushup(node);
 }
 int query(int node,int l,int r){//查询函数 
// 	cout<<start<<" "<<end<<"\n"<<l<<" "<<r<<"?\n";
  
  if(l<=tree[node].l&& tree[node].r<=r){//如果查询的区间在我们的范围内
    return tree[node].sum;//直接返回这个节点的值 
  }
  pushdown(node);//如果不是的话,先使将这个节点的标记传递,并且改变数值来维护线段树的区间修改。
  int m=(tree[node].r+tree[node].l)>>1;
  int res=0;
  if(r>m) res+=query(node<<1|1,l,r);//查右边边
  if(l<=m) res+=query(node<<1,l,r); //左边
  return res;	 
  
 }
signed main(){
  int t;
  cin>>t;
  while(t--){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
      //cout<<i<<"\n";
      scanf("%1d",&cs[i]);//还可以这样用啊!又看博客学到了 
    }
    for(int i=1;i<=n;i++){
      //cout<<i<<"\n";
      scanf("%1d",&a[i]);
    }
    for(int i=1;i<=k;i++){
      
      cin>>b[i].x>>b[i].y;
    }
    
    build(1,1,n);
    
    bool flag=1;
    for(int i=k;i>=1;i--){//倒过来操作 ,看题解说正向操作,区间不同,但是反向复原的话,就能保证区间染色一致 
      int l=b[i].x,r=b[i].y;
      int num=(r-l)>>1;//区间有多少数字,方便查询 
      int one=query(1,l,r);
      int zero=r-l+1-one;//剩下的肯定是零的数量
      if(zero<=num){//零的数量没有超过一般
        update(1,l,r,1);
        
//				cout<<"?";
      }
      else if(one<=num){
        update(1,l,r,0);
      }
      else{
        flag=0;//数量是对半分,那就不能改,出事了 
        break;
      }
//			for(int i=1;i<=n<<2;i++) cout<<tree[i].sum;
//				cout<<"\n";
    }
    for(int i=1;i<=n;i++){
      int z=query(1,i,i);
      if(cs[i]!=z){
        flag=0;
        break;
      }
    }
    if(flag) cout<<"YES\n";
    else cout<<"NO\n"; 
  }
}
posted @ 2021-02-10 12:04  RuanCat  阅读(68)  评论(0)    收藏  举报