题意就是,给定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; }