bzoj 2653 middle 二分+ 可持久化数据结构(可持久化感悟)

本以为可持久化数据结构掌握的还不错,通过此题 我顿时觉得 可持久化数据结构比我想想的要强大。

本以为只有在用到  树上的减法才会用到可持久化数据结构的,如动态区间第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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值