题意就是,给定n,m.
求n个数,满足m个条件。
每个条件的形式是,给定 Li,Ri,Qi ,要求 a[Li]&a[Li+1]&...&a[Ri] = Qi ;
Qi 最多有30个二进制位。所以每个数都可以用int存下。
对于Qi 的每一位,
若为1,则 a[Li],a[Li+1],...,a[Ri]的这一位都必须为1;(按位或)
若为0,则 a[Li],a[Li+1],...,a[Ri]至少有一位为零;(尽量让它们全零,以免多出的几个1,使得其他区间本不应全1的出现了全1)
用线段树,将 Li 到 Ri 区间内的所有数,对Qi 进行 按位或运算。
这样可以用最少数量的1,满足:若Qi的某位为1,则区间[Li,Ri]内的所有数的这一位都是1.
但是由于不同的条件填的1可能相互影响,所以,若Qi的某位为0,则区间[Li,Ri]内的数的这一位不一定有0出现,所以要重新判断。
需再建一个线段树,来存[Li,Ri]内所有数的 &(按位与运算)。
再搜索一次所有的条件,看看是否都满足 a[Li]&a[Li+1]&...&a[Ri] = Qi ;
由于生成这n个数的时候用的是最少个数的1,所以,若不能都满足条件,则不存在满足条件的 n 个数。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define maxn 100001
using namespace std;
//线段树
int N,n,m;
int set[maxn<<2];
void ST_Init(){
N=1;while(N <n+2) N <<=1;
memset(set,0,sizeof(set));
}
void TurnOn(int L,int R,int C){
for(int s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1){
if(~s&1) set[s^1]|=C;
if(t&1) set[t^1]|=C;
}
}
void ShutDown(){
//将设置标记传递下去
for(int i=1;i<N;++i){
if(set[i]){
set[i<<1]|=set[i];
set[i<<1|1]|=set[i];
set[i]=0;
}
}
//重建新的线段树
for(int i=N-1;i>0;--i){
set[i]=set[i<<1]&set[i<<1|1];
}
}
int Query(int L,int R){
int ANS=-1;
for(int s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1){
if(~s&1) ANS&=set[s^1];
if(t&1) ANS&=set[t^1];
}
return ANS;
}
//记录操作
int l[maxn],r[maxn],q[maxn];
int main(void)
{
while(~scanf("%d%d",&n,&m)){
ST_Init();
for(int i=0;i<m;++i){
scanf("%d%d%d",&l[i],&r[i],&q[i]);
//区间内 对q[i]进行按位或
TurnOn(l[i],r[i],q[i]);
}
//重建线段树,存区间的按位与
ShutDown();
//重新判断是否符合m个条件
int T=0;
for(int i=0;i<m;++i){
if(Query(l[i],r[i])==q[i]) ++T;
else break;
}
if(T==m){//如果符合m个条件,输出结果
printf("YES\n");
printf("%d",set[N+1]);
for(int i=2;i<=n;++i){
printf(" %d",set[N+i]);
}
printf("\n");
}
else{//如果不符合m个条件,则不存在符合条件的n个数。
printf("NO\n");
}
}
return 0;
}