1. 程式人生 > >【線段樹】資料結構

【線段樹】資料結構

【描述】

在看了 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');
		}
	}
}