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;
}