【线段树与区间或】 woj 2820

描述

构造一个长度为n的非负整数序列x,满足m个条件,第i个条件为x[li]|x[li+1]|…|x[ri]=pi。

输入

第一行两个整数n,m。接下来m行每行三个整数li,ri,pi。

输出

如果存在这样的序列x,第一行输出Yes,第二行输出n个不超过2^30-1的非负整数表示x[1]~x[n],否则输出一行No。

样例输入[复制]

2 1
1 2 1

样例输出[复制]

Yes
1 1

提示

对于30%的数据,n,m<=1000。

对于另外30%的数据,pi<=1。

对于100%的数据,n,m<=100000,1<=li<=ri<=n,0<=pi<2^30。

如果拿到没有什么头绪的题,可以先看一下部分数据,尝试分析,然后推广到100%的数据。

对于这道题,先看一下pi<=1的情况:

如果pi=0,那么li到ri都必须是0,如果pi=1,那么li到ri至少有一个1。我们可以先把这个序列全部变成1,然后把pi为0的区间赋成0,最后再判一下是否合法。可以用线段树来维护这个序列,支持区间修改和区间查询。时间复杂度O(mlogn)。

30分到手了。那么考虑一下所有的情况。

我们注意到或运算它的每一位是独立的!就相当于是  这个序列的二进制下的每一位  都是一个pi<=1的问题。

可以先把xi的每一位都设成1:把x序列初始化成(1<<30-1)。对于每个条件,把x[li]...x[ri]中的pi=0的位置【二进制下】设成0,最后判断一下区间的 或 是否等于pi。同样用线段树维护。时间复杂度O(mlogn)。

#include<bits/stdc++.h>
using namespace std;
const int MAX=101000;
int l[MAX],r[MAX],p[MAX],ans[MAX<<1];
int n,m,maxn=1;
void read(int &x){
	x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}

void add(int root,int l,int r,int L,int R,int C){
	if(L<=l&&R>=r){
		ans[root]&=C;
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) add(root<<1,l,mid,L,R,C);
	if(R>mid) add(root<<1,mid+1,r,L,R,C);
}

int query(int root,int l,int r,int L,int R){
	if(L<=l&&R>=r)
	return ans[root];
	int mid=(l+r)>>1;
	int ret=0;
	if(L<=mid) ret|=query(root<<1,l,mid,L,R);
	if(R>mid) ret|=query(root<<1|1,mid+1,r,L,R);
	return ret;
}
int main(){
/*	freopen("or.in","r",stdin);
	freopen("or.out","w",stdout);*/
	read(n),read(m);
	for(;maxn<n;maxn<<=1);
	for(int i=1;i<maxn*2;i++) ans[i]=(1<<30)-1;
	for(int i=1;i<=m;++i){
		read(l[i]),read(r[i]),read(p[i]);
		add(1,1,maxn,l[i],r[i],p[i]);
	}
	//add函数只会更改几个结点的区间或,不会更新其子树的或。

	for(int i=1;i<maxn;++i){
		ans[i<<1]&=ans[i];
		ans[i<<1|1]&=ans[i];
	}
    //从上往下把子树区间的或更新一下。

	for(int i=maxn-1;i>=1;--i)
		ans[i]=ans[i<<1]|ans[i<<1|1];
    //从下往上把父亲区间的或更新一下
	

	for(int i=1;i<=m;++i){
		if(query(1,1,maxn,l[i],r[i])!=p[i]){
			printf("No\n");
			return 0;
		}
	}
	printf("Yes\n");
	for(int i=1;i<=n;++i)
		printf("%d ",ans[maxn+i-1]);
    //maxn是最后一层【叶节点】的第一个下标。
    //ans[maxn]对应x[1]。从ans[maxn]开始的n个就是x[1]到x[n]。
	putchar('\n');
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/83049770
今日推荐