本以为可持久化数据结构掌握的还不错,通过此题 我顿时觉得 可持久化数据结构比我想想的要强大。
本以为只有在用到 树上的减法才会用到可持久化数据结构的,如动态区间第K大 这种 明显就是用了 区间属性 可以 两个 前项和的属性 相减得到 。
此时 可持久化数据结构是 按 下标建树,构建了n个版本的树。第 K 棵树 表示的是 区间 1-K 的属性。
第K版本的意义并非只有 区间 1-K。
完全可以按值建树,构建 n个版本,可以对 某个版本进行查询。
可持久化线段数中:
在可持久化数据结构中 如果更新一棵树 update(int pos,int left ,int right ,Node *root) 此时 root是指的要跟新本棵树。(点更新操作的update 或者 build 一整棵树的build)。
二、 由旧的树构建出新的树时,root 指的是上一个版本的树。
二分 最大值,
对于给定的K ,判断是不是最大值,是此题的关键。
把 比 K 小的标记为 -1 ,其余的标记为 1。
查询 左端点在【a,b】 ,右端点在【c,d】 的最大子段和。 如果结果小于0,说明给定 K 偏大。直到找到一个K的结果 刚好大于或等于0 且 K+1的结果小于0.
此时就找到K 就是最大的 中间值。
问题是 现在 K 是变化的, 按值排序后,值映射到1-n ,也就是 K 从1 变化到n.
对于K =1 时,下标 1- n 中每个数都是 1.
仔细想想便得知 :
K 从 i 变成 i+1时,仅 i 这个数 的 位置(也就是原来未排序之前的下标) 由1 变成 -1.
举个例子
2 4 1 5 3
第一个版本 1 1 1 1 1 K=1
第二个版本 1 1 -1 1 1 K=2
第三个版本 -1 1 -1 1 1 K= 3
第四个版本 -1 1 -1 1 -1 K =4
第五个版本 -1 -1 -1 1 -1 K=5
这样问题就简单了。
首先构建整棵树。
然后对于每个版本就上一个版本中 更改一个点。 增添lgn 个节点,构建出新树。
然后查询的时候 对于给定的K查询时,直接在这个版本中 查询就可。 (这种意思上更像以前峰哥简单的线段树。)
此题中感觉 二分 十分强大。。。。。 想不到二分。。o(︶︿︶)o 唉 即使想到二分 再去想 数据结构时 也无从下手。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <cstring>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <assert.h>
#include <queue>
#define REP(i,n) for(int i=0;i<n;i++)
#define TR(i,x) for(typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define ALLL(x) x.begin(),x.end()
#define SORT(x) sort(ALLL(x))
#define CLEAR(x) memset(x,0,sizeof(x))
#define FILLL(x,c) memset(x,c,sizeof(x))
using namespace std;
const double eps = 1e-9;
#define LL long long
#define pb push_back
const int maxn = 21000;
struct S{
int v;
int i;
bool operator < (const S &b)const{
return v<b.v;
}
}s[maxn];
int num[maxn];
int idx[maxn];
int n;
map<int,int>mp;
map<int,int>::iterator it;
int q[4];
int Q;
//
struct Node{
Node *l ,*r;
int v;
int lx,rx,sum;
};
struct Seg{
int C;
Node nodes[maxn*80];
Node *root[maxn];
Node *null;
void init(){
C =0;
null = &nodes[C++];
null->l = null->r = null;
null ->lx =null->sum = null->rx = null->v = 0;
root[0] = null;
root[1] = null;
}
void pushup(Node *rt){
rt->lx = max(rt->l->lx,rt->l->sum+rt->r->lx);
rt->rx = max(rt->r->rx,rt->r->sum+rt->l->rx);
rt->sum = rt->l->sum + rt->r->sum;
}
void pushup(Node &rt,Node &rt2,Node &rt3){
rt.lx = max(rt2.lx,rt2.sum+rt3.lx);
rt.rx = max(rt3.rx,rt3.sum+rt2.rx);
rt.sum = rt2.sum + rt3.sum;
}
Node* build(int left,int right,Node *root){
Node *rt = root;
if(rt == null){
rt = &nodes[C++];
rt->l = rt->r = null;
rt->sum = rt->lx = rt->rx = 0;
}
if(left ==right){
rt->lx = rt ->sum = rt->rx = 1;
return rt;
}
int mid = (left +right)/2;
rt->l = build(left,mid,rt->l);
rt->r = build(mid+1,right,rt->r);
pushup(rt);
return rt;
}
// 版本的更新root 指的是上一棵树。 而对于树上的更新是指 本棵树。所以这里是上一棵树。
Node *update(int pos,int left ,int right,Node *root){
Node *rt = &nodes[C++];
rt->l = root->l;
rt->r = root->r;
rt->lx = root->lx;
rt->rx = root->rx;
rt->sum = root->sum;
if(left == right && pos == left){
rt->lx = rt->rx = rt->sum = -1;
return rt;
}
int mid = (left +right)/2;
if(pos<=mid){
rt->l = update(pos,left,mid,root->l);
}else{
rt->r = update(pos,mid+1,right,root->r);
}
pushup(rt);
return rt;
}
Node query(int left ,int right,int L ,int R,Node *root){
if(L>R)return *null;
if(L<=left && right<= R){
return *root;
}
int mid = (left + right)/2;
Node a,b,c;
a= b=c = (*null);
if(mid>=R){
return query(left,mid,L,R,root->l);
}
if(mid<L){
return query(mid+1,right,L,R,root->r);
}
b= query(left,mid,L,R,root->l);
c = query(mid+1,right,L,R,root->r);
pushup(a,b,c);
return a;
}
void solve(){
root[1] = build(1,n,root[1]);
for(int i=2;i<=n;i++){
root[i] = update(s[i-1].i,1,n,root[i-1]);
// cout << root[i]->sum <<endl;
}
}
bool check(int mid){
Node a = query(1,n,q[1]+1,q[2]-1,root[mid]);
Node b = query(1,n,q[0],q[1],root[mid]);
// cout << "fuck"<<b.rx<<endl;
Node c = query(1,n,q[2],q[3],root[mid]);
int ans = a.sum + b.rx + c.lx;
// cout << a.sum <<" "<<b.rx<<" "<<c.lx<<endl;
// cout << ans << " check "<<mid<<endl;
if(ans>=0)return true;
return false;
}
}Seg;
void solve(){
Seg.init();
Seg.solve();
scanf("%d",&Q);
int last_ans= 0;
while(Q--){
for(int i=0;i<4;i++){
scanf("%d",&q[i]);
q[i]+=last_ans;
q[i]%=n;
q[i]+=n;
q[i]%=n;
q[i]+=1;
}
sort(q,q+4);
//cout << q[0]<< " "<<q[1]<<" "<<q[2]<<" "<<q[3]<<endl;
int left = 1,right=n;
if(Seg.check(right)){
left = n+1;
}else
while(left<right){
//cout << left << " mid "<<right <<endl;
int mid = (left +right)/2;
if(Seg.check(mid)){
left = mid+1;
}else{
right = mid;
}
}
printf("%d\n",idx[left-1]);
last_ans = idx[left-1];
}
}
//
int main(){
while(~scanf("%d",&n)){
mp.clear();
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
mp[num[i]]=1;
}
int tot=0;
for(it=mp.begin();it != mp.end();it++){
tot++;
it->second = tot;
idx[tot] = it->first;
}
for(int i=1;i<=n;i++){
s[i] = (S){mp[num[i]],i};
}
sort(s+1,s+n+1);
solve();
}
return 0;
}