1. 程式人生 > >NOIP模擬 資料結構(線段樹)

NOIP模擬 資料結構(線段樹)

【題目描述】

在看了 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 的總和不超過 5e6。

~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,無特殊限制。

【題目分析】

這道題可以跑4s。。。。最後總共跑了9999ms簡直暗示著什麼。。。。。。

好了不扯了,反正又是道不可做題(恭喜WCR大佬暴力50+RANK1),下來看了看題解還算是基本能搞懂,最後各種TLE,然後發現加個inline就過了。。。。花式難受。。。。。

講講正解:維護一顆線段樹,讓他能記錄區間最小值以及區間最小值的位置。所以整個線段樹用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;
}