[BZOJ2653]middle
[BZOJ2653]middle
題面
一個長度為n的序列a,設其排過序之后為b,其中位數定義為b[n/2],其中a,b從0開始標號,除法取下整。給你一個
長度為n的序列s?;卮餛個這樣的詢問:s的左端點在[a,b]之間,右端點在[c,d]之間的子序列中,最大的中位數。
其中a<b<c<d。位置也從0開始標號。我會使用一些方式強制你在線。
Input
第一行序列長度n。接下來n行按順序給出a中的數。
接下來一行Q。然后Q行每行a,b,c,d,我們令上個詢問的答案是
x(如果這是第一個詢問則x=0)。
令數組q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
將q從小到大排序之后,令真正的
要詢問的a=q[0],b=q[1],c=q[2],d=q[3]?! ?/p>
輸入保證滿足條件。
第一行所謂“排過序”指的是從小到大排序!
n<=20000,Q<=25000
Output
Q行依次給出詢問的答案。
Sample Input
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
271451044
969056313
思路
這道題也是蠻有意思的。我們思考一下,對于單次查詢該怎么做。
這里有一個針對中位數的數據結構題的小trick:把大于x的設置為1,小于x的設置為-1,等于x的設置為0。很顯然,如果\(sum\)大于0則中位數比x更大,小于0則中位數比x更小,等于0則中位數為x。(在這道題中我們可以把\(sum\)為0的情況歸類到中位數大于x里面,原因下面會說)
有了上面這個小trick,我們就可以判斷任何一個數和中位數的關系。那么很顯然這里可以使用二分。那么問題來了,二分的復雜度是\(O(\log n)\),如果我們對于每一次二分都重新求和,復雜度是\(O(n)\),對于q次查詢,總復雜度為\(O(qn\log n)\),很顯然是要炸的。
我們發現復雜度里的\(O(q \log n)\)是不能省的(強制在線的查詢和二分),那我們就要試圖做到\(\log n\)判斷一個數是否為中位數。
我們發現,在二分尋找中位數時,不是每一次判斷都需要修改全樹的。在每次縮小查詢范圍后(l或r變為mid),范圍外的數和下一次二分的數的大小關系是始終不變的,所以我們可以繼承上一次修改的結果而不需要再次修改。
另外,對于這種多次查詢又是強制在線的數據結構題,我們可以把所有潛在答案計算出來,再用可持久化數據結構儲存。
那么整理一下思路,解法如下:
- 先構建一個可持久化線段樹,每個葉子節點對應一個序列上的位置,節點初始值為1(畢竟最開始二分的值是無限小)。
- 按照從小到大的順序加入元素(值為-1)。每加入一次就相當于現在中位數可能的值增加。
- 對于每次查詢,我們可以用線段樹操作\(maxl(a,b)+sum(b,c)+maxr(c,d)\)來求得中位數的“判定值”。
- 對于每次查詢,二分一下答案對應的版本即可。
代碼
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn (int)(2e4+1000)
#define ll long long
using namespace std;
int n,q,idx;
ll sum[maxn<<5],lmax[maxn<<5],rmax[maxn<<5];
int ls[maxn<<5],rs[maxn<<5],rt[maxn],pre,b[7];
struct gg{int num,loc;
}a[maxn];
void push_up(int x){sum[x]=sum[ls[x]]+sum[rs[x]];lmax[x]=max(sum[ls[x]]+lmax[rs[x]],lmax[ls[x]]);rmax[x]=max(sum[rs[x]]+rmax[ls[x]],rmax[rs[x]]);return;
}
int build(int l,int r){int now=++idx;if(l==r){sum[now]=lmax[now]=rmax[now]=1;return now;}int mid=(l+r)>>1;ls[now]=build(l,mid);rs[now]=build(mid+1,r);push_up(now);return now;
}
bool cop(gg x,gg y){return x.num<y.num;}int update(int last,int l,int r,int tar,int val){int now=++idx;lmax[now]=lmax[last]-2,rmax[now]=rmax[last]-2,sum[now]=sum[last]-2;ls[now]=ls[last],rs[now]=rs[last];if(l==r){return now;}int mid=(l+r)>>1;if(tar<=mid)ls[now]=update(ls[last],l,mid,tar,val);else{rs[now]=update(rs[last],mid+1,r,tar,val);}push_up(now);return now;
}
ll get_sum(int now,int l,int r,int tl,int tr){if(tl<=l&&r<=tr)return sum[now];int mid=(l+r)>>1;ll ans=0;if(tl<=mid)ans+=get_sum(ls[now],l,mid,tl,tr);if(tr>=mid+1)ans+=get_sum(rs[now],mid+1,r,tl,tr);return ans;
}
ll get_rmax(int now,int l,int r,int tl,int tr){if(r<l)return 0;int mid=(l+r)>>1;if(tl<=l&&r<=tr)return rmax[now];if(tr<=mid)return get_rmax(ls[now],l,mid,tl,tr);if(tl>=mid+1)return get_rmax(rs[now],mid+1,r,tl,tr);return max(get_sum(rs[now],mid+1,r,tl,tr)+get_rmax(ls[now],l,mid,tl,tr),get_rmax(rs[now],mid+1,r,tl,tr));
}
ll get_lmax(int now,int l,int r,int tl,int tr){if(r<l)return 0;int mid=(l+r)>>1;if(tl<=l&&r<=tr)return lmax[now];if(tr<=mid)return get_lmax(ls[now],l,mid,tl,tr);if(tl>=mid+1)return get_lmax(rs[now],mid+1,r,tl,tr);return max(get_sum(ls[now],l,mid,tl,tr)+get_lmax(rs[now],mid+1,r,tl,tr),get_lmax(ls[now],l,mid,tl,tr));
}
bool check(int ves){ves-=1;ves=rt[ves];llans=get_sum(ves,1,n,b[2],b[3])+max(get_rmax(ves,1,n,b[1],b[2]-1),0ll)+max(0ll,get_lmax(ves,1,n,b[3]+1,b[4]));if(ans>=0)return 1;else return 0;
}
int main(){
// freopen("in","r",stdin);scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i].num),a[i].loc=i;rt[0]=build(1,n);sort(a+1,a+1+n,cop);for(int i=1;i<=n;i++){rt[i]=update(rt[i-1],1,n,a[i].loc,-2);}scanf("%d",&q);for(int i=1;i<=q;i++){for(int j=1;j<=4;j++){scanf("%d",&b[j]);b[j]=(b[j]+pre)%n;b[j]++;}sort(b+1,b+5);int l=0,r=n;while(l<r){int mid=(l+r+1)>>1;if(check(mid)){l=mid;}else{r=mid-1;}}printf("%d\n",a[l].num);pre=a[l].num;}return 0;
}
轉載于:https://www.cnblogs.com/GavinZheng/p/10862793.html
總結
以上是生活随笔為你收集整理的[BZOJ2653]middle的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国画牧牛图是谁画的啊?
- 下一篇: Flask 启动配置