1. 程式人生 > >【線段樹與區間或】 woj 2820

【線段樹與區間或】 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');
}