【线段树】数据结构

【描述】

在看了 jiry_2 的课件《Segment Tree Beats!》后,小 O 深深沉迷于这种能单次 O(logn) 支持区间与一个数取 min/max,查询区间和等信息的数据结构,于是他决定做一道与区间与 一个数取 min/max 的好题。

这题是这样的:你得到了一个长度为 n 的数列{ai},要求支持以下 2 种操作:第一种是给 定 L,R,X,要求把区间中比 X 小的数字全部修改为 X;第二种是给定 L,R,K,X,查询区间中比 X 小的最小的 K 个数,并且将它们升序输出,没有则输出-1。

小 O 觉得这题太简单了,于是把这题丢给了你,请你帮忙实现。

下发文件中有 jiry_2 的课件《Segment Tree Beats!》,不保证其与解题有关。

【输入】

第一行一个数字 n 表示数列长度,

第二行 n 个数字分别表示 a1....an,

第三行一个数字 m 表示操作次数,

接下来 m 行每行表示一次操作,

第一个数 op 表示操作类型,op 可能是 1 或 2,

如果 op=1,后面有 L,R,X 三个正整数表示把区间[L,R]中比 X 小的数字全部改成 X

如果 op=2,后面有 L,R,X,K 四个正整数表示查询区间[L,R]中比 X 小的最小的 K 个数

【输出】

对于每个 op=2,输出一行,

如果比 X 小的数达到了 K 个,升序输出最小的 K 个数,

如果比 X 小的数小于 K 个,输出一行一个-1 即可.

【样例输入】[复制]

3
1 2 3
4
1 1 2 2
2 1 3 1 3
2 1 3 2 1
2 1 3 3 2

【样例输出】[复制]

-1
-1
2 2 

【提示】

本题共 6 个测试点,不采用 subtask 评测,但每个测试点分值不同。

对于全部数据,满足 1<=n,m<=500000,1<=L<=R<=n,1<=K<=n,1<=Ai,X<=10^9,对于所有 操作 2 中的 K,K 的总和不超过 5*10^6。

~1:12pts,满足 1<=n,m<=3000;

~2:7pts,满足 1<=n,m<=100000,没有操作 1,且对于所有操作 2 有 K=1;

~3:23pts,满足 1<=n,m<=100000,对于所有操作 2 有 K=1;

~4:37pts,满足 1<=n,m<=100000,没有操作 1;

~5:6pts,满足 1<=n,m<=100000;

~6:15pts,无特殊限制。

如果没有修改操作可以用权值线段树,然后再分段跑个3000的数据,12+7+37=56。如果还有时间的话可以写一个线段树,支持区间修改和区间查询最小值。56+23=79。。。骗分技巧还是很重要的。。。

【来自GLF】正解:

维护一颗线段树,让它能记录区间最小值以及区间最小值的位置,所以整个线段树用pair类型存储,第一位存储区间最小值,第二位存储区间最小值的位置,对于所有操作1,我们发现就是在给定区间里让所有数与x取个max,如果当前区间最小值已经大于x,那么显然这个操作不会产生影响,否则我们将当前区间最小值设为x即可。

对于所有操作2,因为给定了sigmaK<=5e6,所以我们可以直接寻找K次,每一次寻找了区间最小值后,将区间最小值改为INF,然后更新,再次询问,如果在K次询问内区间最小值大于X,那么这个操作是非法的。每一次找到的最小值信息用一个vector维护,询问完成后再重新更新回去即可。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const int INF=0x3f3f3f3f;
pair<int,int> minn[MAXN<<2];
int tag[MAXN<<2];
int n,m,q;
int a[MAXN];
inline int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}
inline void sc(int x)
{
	if(x>=10)
	  sc(x/10);
	putchar(x%10+48);
}
inline void push_up(int k) {minn[k]=min(minn[k<<1],minn[k<<1|1]);}
inline void push_now(int k,int val) {minn[k].first=max(minn[k].first,val);}
inline void push_down(int k)
{
	push_now(k<<1,minn[k].first);
	push_now(k<<1|1,minn[k].first);
}

inline void build(int root,int l,int r)
{
	if(l==r)
	{
		minn[root].first=Read();
		minn[root].second=l;
		return ;
	}
	int mid=l+r>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	push_up(root);
}

inline void update(int k,int l,int r,const int &ql,const int &qr,const int &key)
{
	if(minn[k].first>key)
	  return ;
	if(ql<=l&&r<=qr)
	  return push_now(k,key);
	push_down(k);
	int mid=(l+r)>>1;
	if(ql<=mid)
	  update(k<<1,l,mid,ql,qr,key);
	if(mid<qr)
	  update(k<<1|1,mid+1,r,ql,qr,key);
	push_up(k);
}

inline void modify(int k,int l,int r,const int &pos,const int &key)
{
	if(l==r)
	  return (void)(minn[k].first=key);
	push_down(k);
	int mid=(l+r)>>1;
	if(pos<=mid)
	  modify(k<<1,l,mid,pos,key);
	else 
	  modify(k<<1|1,mid+1,r,pos,key);
	push_up(k);
}

inline pair<int,int> querymin(int k,int l,int r,const int &ql,const int &qr)
{
	if(ql<=l&&r<=qr)
	  return minn[k];
	push_down(k);
	int mid=(l+r)>>1;
	if(mid<ql)
	  return querymin(k<<1|1,mid+1,r,ql,qr);
	if(qr<=mid)
	  return querymin(k<<1,l,mid,ql,qr);
	return min(querymin(k<<1,l,mid,ql,qr),querymin(k<<1|1,mid+1,r,ql,qr));
}

int main()
{
	n=Read();
	build(1,1,n);
	q=Read();
	while(q--)
	{
		int cz=Read(),l=Read(),r=Read(),x=Read();
		if(cz==1)
		  update(1,1,n,l,r,x);
		else
		{
			int k=Read();
			if(r-l+1<k)
			{
				puts("-1");
				continue;
			}
			vector<pair<int,int> > vec;
			bool flag1=true;
			for(int i=1;i<=k;++i)
			{
				pair<int,int> tmp=querymin(1,1,n,l,r);
				if(tmp.first>=x)
				  break;
				vec.push_back(tmp);
				modify(1,1,n,tmp.second,INF);
			}
			if(vec.size()==k)
			{
				for(int i=0;i<k;++i)
				  sc(vec[i].first),putchar(' ');
				puts("");
			}
			else
			  puts("-1");
			for(int i=0;i<vec.size();++i)
			  modify(1,1,n,vec[i].second,vec[i].first);
		}
	}
	return 0;
}

再贴一个考场权值线段树暴力:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2333333;
int n,m,l,r,x,k,q,t=0,op;
int a[maxn],b[maxn],root[maxn];
struct node{
	int lc,rc,sum;
}tr[maxn];
inline 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 sc(int x){
	if(x>9) sc(x/10);
	putchar(x%10+48);
}
void disc(){
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
}
void Insert(int y,int &x,int l,int r,int p){
	x=++t;
	tr[x]=tr[y];
	tr[x].sum++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(p<=mid) Insert(tr[y].lc,tr[x].lc,l,mid,p);
	else Insert(tr[y].rc,tr[x].rc,mid+1,r,p);
}
int query(int x,int y,int l,int r,int k){
	if(l==r) return l;
	int delta=tr[tr[y].lc].sum-tr[tr[x].lc].sum;
	int mid=(l+r)>>1;
	if(delta>=k) return query(tr[x].lc,tr[y].lc,l,mid,k);
	else return query(tr[x].rc,tr[y].rc,mid+1,r,k-delta);
}
void work(int x,int y,int l,int r,int k){
	if(l==r){
		for(int i=1;i<=tr[y].sum-tr[x].sum;++i) sc(b[l]),putchar(' ');
		return;
	}
	int delta=tr[tr[y].lc].sum-tr[tr[x].lc].sum;
	int mid=(l+r)>>1;
	if(delta>=k) work(tr[x].lc,tr[y].lc,l,mid,k);
	else{
		work(tr[x].lc,tr[y].lc,l,mid,delta);
		work(tr[x].rc,tr[y].rc,mid+1,r,k-delta);
	}
}
void solve1(){
	while(q--){
		read(op);
		if(op==1){
			read(l),read(r),read(x);
			for(int i=l;i<=r;++i)
				if(a[i]<x)
					a[i]=x;
		}
		if(op==2){
			read(l),read(r),read(x),read(k);
			memcpy(b,a+l,(r-l+1)*4);
			sort(b,b+r-l+1);
			if(b[k-1]>=x) puts("-1");
			else{
				for(int i=0;i<k;++i)
					sc(b[i]),putchar(' ');
				putchar('\n');
			}
		}
	}
}
int main(){
	read(n);
	for(int i=1;i<=n;++i) read(a[i]),b[i]=a[i];
	read(q);
	if(n<=3000&&q<=3000) return solve1(),0;
	disc();for(int i=1;i<=n;++i) Insert(root[i-1],root[i],1,m,a[i]);
	while(q--){
		read(op);
		if(op==1){
			read(l),read(r),read(x);
		}
		if(op==2){
			read(l),read(r),read(x),read(k);
			int N=b[query(root[l-1],root[r],1,m,k)];
			if(x<=N) puts("-1");
			else work(root[l-1],root[r],1,m,k),putchar('\n');
		}
	}
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/83032224