Codeforces 482B Interesting Array(位运算+差分+线段树)

巧妙的题目。

Interesting Array

time limit per test: 1 second
memory limit per test:256 megabytes

We’ll call an array of n non-negative integers a[1], a[2], …, a[n] interesting, if it meets m constraints. The i-th of the m constraints consists of three integers li, ri, qi (1 ≤ li ≤ ri ≤ n) meaning that value should be equal to qi.

Your task is to find any interesting array of n elements or state that such array doesn’t exist.

Expression x&y means the bitwise AND of numbers x and y. In programming languages C++, Java and Python this operation is represented as “&”, in Pascal — as “and”.

Input
The first line contains two integers n, m (1 ≤ n ≤ 105, 1 ≤ m ≤ 105) — the number of elements in the array and the number of limits.

Each of the next m lines contains three integers li, ri, qi (1 ≤ li ≤ ri ≤ n, 0 ≤ qi < 230) describing the i-th limit.

Output
If the interesting array exists, in the first line print “YES” (without the quotes) and in the second line print n integers a[1], a[2], …, a[n] (0 ≤ a[i] < 230) decribing the interesting array. If there are multiple answers, print any of them.

If the interesting array doesn’t exist, print “NO” (without the quotes) in the single line.

Examples
input
3 1
1 3 3
output
YES
3 3 3
input
3 2
1 3 3
1 3 2
output
NO

首先嘛,如果一段区间的逻辑与(&)的和为q,那么这段区间内所有的数的二进制下,所有q的二进制数为1的数位,这些数位都必须为1。

比如q=5,那么对应的二进制数为101,这样这段区间所有的数的第0和第2位都得是1。

这样的话,我们利用一个差分数组s[i][j](表示数组第i位的二进制的第j位是否得取1)。然后就可以快速的把a[1…n]这个区间所有的数求出来了(利用或运算)。

最后线段树验证区间&和就行。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int n,m;
struct node{
	int x,y,q;
}s[maxn];
int a[maxn];
int part[maxn][40];
int t[maxn<<2];
void update(int x){
	t[x]=t[x<<1]&t[x<<1|1];
}
void build(int l,int r,int rt){  
	if (l==r){
		t[rt]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	update(rt);
}
int query(int ll,int rr,int l,int r,int rt){
	if (l>=ll&&r<=rr){
		return t[rt];
	}
	int mid=(l+r)>>1;
	if (rr<=mid) return query(ll,rr,l,mid,rt<<1);
	else if (ll>mid) return query(ll,rr,mid+1,r,rt<<1|1);
	else return query(ll,rr,l,mid,rt<<1)&query(ll,rr,mid+1,r,rt<<1|1);
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for (int i=1;i<=m;i++){
		cin>>s[i].x>>s[i].y>>s[i].q;
	}
	//差分过程
	for (int i=1;i<=m;i++){
		for (int j=0;j<30;j++){
			if (s[i].q&(1<<j)){
				part[s[i].x][j]++;
				part[s[i].y+1][j]--;
			}
		}
	}
	//求a[i]
	for (int j=0;j<30;j++){
		for (int i=1;i<=n;i++){
			part[i][j]+=part[i-1][j];
			if (part[i][j]) a[i]=a[i]|(1<<j);
		}
	}
	//线段树验证
	build(1,n,1);
	for (int i=1;i<=m;i++){
		int x=query(s[i].x,s[i].y,1,n,1);
		if (x!=s[i].q) {
			cout<<"NO\n";
			return 0;
		}
	}
	cout<<"YES\n";
	for (int i=1;i<=n;i++) cout<<a[i]<<" ";
	cout<<endl;
	return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值