2017西安交大ACM小学期数据结构 [线段树]

Problem B+

发布时间: 2017年7月1日 02:08   最后更新: 2017年7月1日 02:10   时间限制: 1000ms   内存限制: 64M

给定一个长度为 n 的序列 a1 a2 , ...,  an , 满足这个序列是一个 1 ~ n 的排列

如果一个序列满足: 将序列排序后, 任意两个相邻的元素的差为 1 , 那么就称这个序列为"胖虎序列"

给出 q 个操作, 操作分为两种

对于形如 1   x   y 的操作, 交换 ax ay , 满足 1x<yn

对于形如 2   x   y 的操作, 判断区间 [x,y] 内的元素构成的序列是否为一个"胖虎序列", 如果是, 输出"YES", 否则输出"NO", 满足 1xyn

9×104n105 9×104q105

第一行两个整数 n q , 意义如上所述。
第二行 n 个整数, 表示序列 a
接下来 q 行, 每行第一个数为 opt , 之后紧跟两个数, 意义如上所述。

对于每个操作 2 , 输出答案, 一行一个。

  复制
5 3
1 2 4 3 5
2 1 3
1 3 4
2 1 3
NO
YES
这是一道比较有意思的题,题目给出一个1到n的排列,然后两种操作:

(1)交换任何两个数

(2)选取一段连续的区间,并判断能否构成等差数列且 公差为1


判断等差序列的时候,我们先找到这段序列的最小值以及最大值,然后假设其为等差序列,估计出区间和,然后和维护的真实区间和进行比对,如果相等就是等差序列,否则的话,就不是

我们用到的区间求最大值,最小值以及区间和的数据结构都是线段树。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 100007;
const int INF = 1e9;
int st[MAX<<2][3];
int a[MAX];
int n,q;
int ops(int a,int b,int op){
	switch(op){
		case 0:
			return max(a,b);
		case 1:
			return min(a,b);
		case 2:
			return a + b;
	}
}
void build(int cur,int l,int r,int op){
	if(l == r) st[cur][op] = a[l];
	else{
		int mid = (r + l) /2;
		build(cur*2,l,mid,op);
		build(cur*2+1,mid + 1,r,op);
		st[cur][op] = ops(st[cur*2][op],st[cur*2+1][op],op);

	}
}
void modify(int cur,int l,int r,int pos,int val,int op){
	if(l == r) st[cur][op] = val;
	else{
		int mid = (l + r)/2;
		if(pos <= mid) modify(cur*2,l,mid,pos,val,op);
		else modify(cur*2 + 1,mid + 1,r,pos,val,op);
		st[cur][op] = ops(st[cur*2][op],st[cur*2+1][op],op);
	}
}
int query(int cur,int l,int r,int x,int y,int op){
	if(x <= l && y >= r) return st[cur][op];
	int mid = (l+r)/2,res = -INF;
	if(y <= mid) res = query(cur*2,l,mid,x,y,op);
	else if(x > mid){
		res = query(cur*2 + 1,mid + 1,r,x,y,op);
	}else{
		int ls = query(cur * 2, l, mid, x, y,op); 
		int rs = query(cur * 2 + 1, mid + 1, r, x, y,op); 
		res = ops(ls,rs,op);
	}
	return res;
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i = 1;i <= n;i++){
		scanf("%d",&a[i]);
	}
	build(1,1,n,0);
	build(1,1,n,1);
	build(1,1,n,2);
	while(q--){
		int opt,x,y;
		scanf("%d%d%d",&opt,&x,&y);
		if(opt == 1){
			modify(1,1,n,x,a[y],0);
			modify(1,1,n,x,a[y],1);
			modify(1,1,n,x,a[y],2);
			modify(1,1,n,y,a[x],0);
			modify(1,1,n,y,a[x],1);
			modify(1,1,n,y,a[x],2);
			swap(a[x],a[y]);
		}
		else{
			int ma = query(1,1,n,x,y,0);
			int mi = query(1,1,n,x,y,1);
			int s = (ma+mi)*(ma-mi+1)/2;
			if(s == query(1,1,n,x,y,2)){
				cout<<"YES"<<endl;
			}
			else{
				cout<<"NO"<<endl;
			}
		}
	}
	return 0;
} 



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值