天天看點

bzoj4552(線段樹合并+stl||二分+線段樹)

Description

在2016年,佳媛姐姐喜歡上了數字序列。因而他經常研究關于序列的一些奇奇怪怪的問題,現在他在研究一個難題,需要你來幫助他。這個難題是這樣子的:給出一個1到n的全排列,現在對這個全排列序列進行m次局部排序,排序分為兩種:1:(0,l,r)表示将區間[l,r]的數字升序排序2:(1,l,r)表示将區間[l,r]的數字降序排序最後詢問第q

位置上的數字。

Input

輸入資料的第一行為兩個整數n和m。n表示序列的長度,m表示局部排序的次數。1 <= n, m <= 10^5第二行為n個整

數,表示1到n的一個全排列。接下來輸入m行,每一行有三個整數op, l, r, op為0代表升序排序,op為1代表降序

排序, l, r 表示排序的區間。最後輸入一個整數q,q表示排序完之後詢問的位置, 1 <= q <= n。1 <= n <= 10^5

,1 <= m <= 10^5

Output

輸出資料僅有一行,一個整數,表示按照順序将全部的部分排序結束後第q位置上的數字。

Sample Input

6 3

1 6 2 5 3 4

0 1 4

1 3 6

0 2 4

3

Sample Output

5

s o l u t i o n solution solution:

這題有兩種做法,複雜度與思路都不同,但都是比較經典的做法。

做法①:

想法比較暴力

初始對每個點開一棵權值線段樹

每次詢問的時候,把 [ l , r ] [l,r] [l,r]兩端的塊分裂,再把區間内所有的線段樹合并

最後暴力查詢一下,用 s e t set set維護所有的塊

複雜度?

可以發現,我們建立的塊是 O ( n ) O(n) O(n)的

那麼我們合并以及分裂的操作涉及的塊不難發現也是 O ( n ) O(n) O(n)的

總複雜度 O ( n l o g n ) O(nlogn) O(nlogn)

分裂的時候還要分塊的種類,本人使用了 m a p map map

代碼細節較複雜。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define hash Hash
#define fr first
#define se second
#define IT set<P>::iterator
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
int n,m;
int a[101000];
set<P>h;
set<P>::iterator it;
map<P,int>hash;
map<P,int>op;
namespace tree
{
	stack<int>st;
	int cnt_id = 0;
    struct Tree{int ls,rs,num;}tr[10100000];
	int New(){int x;if(!st.empty()) x=st.top(),st.pop();else x=++cnt_id;return x;}
	void free(int x){tr[x].ls = tr[x].rs = tr[x].num = 0;st.push(x);}
    void update(int rt){tr[rt].num = tr[tr[rt].ls].num + tr[tr[rt].rs].num;}
	void build(int &rt,int l,int r,int v)
	{
		rt = New();tr[rt].num++;
		if(l == r) return;
		int mid = l+r>>1;
		if(v <= mid) build(tr[rt].ls,l,mid,v);
		else build(tr[rt].rs,mid+1,r,v);
	}
	int merge(int x,int y)
	{
		if(!x) return y;
		if(!y) return x;
		int t = New();
		tr[t].ls = merge(tr[x].ls,tr[y].ls);
		tr[t].rs = merge(tr[x].rs,tr[y].rs);
		tr[t].num = tr[tr[t].ls].num + tr[tr[t].rs].num;
		free(x);free(y);
		return t;
	}
	void split1(int &x,int y,int k)
	{//分裂出前k大 
	    x = New();
	    if(k > tr[tr[y].rs].num) split1(tr[x].ls,tr[y].ls,k-tr[tr[y].rs].num),swap(tr[x].rs,tr[y].rs);
	    else if(k == tr[tr[y].rs].num) swap(tr[x].rs,tr[y].rs);
	    else split1(tr[x].rs,tr[y].rs,k);
	    update(x);update(y);
	}
	void split2(int &x,int y,int k)
	{//分裂出前k小
	    x = New();
	    if(k > tr[tr[y].ls].num) split2(tr[x].rs,tr[y].rs,k-tr[tr[y].ls].num),swap(tr[x].ls,tr[y].ls);
	    else if(k == tr[tr[y].ls].num) swap(tr[x].ls,tr[y].ls);
	    else split2(tr[x].ls,tr[y].ls,k);
	    update(x);update(y);
	}
	int query(int rt,int l,int r,int k)
	{//第k小
	    if(l == r) return l;
	    int mid = l+r>>1;
		if(k <= tr[tr[rt].ls].num) return query(tr[rt].ls,l,mid,k);
		else return query(tr[rt].rs,mid+1,r,k-tr[tr[rt].ls].num); 
	}
}using namespace tree;
IT fuckit(IT it,int l,int r)
{
	int _l = it->fr,_r = it->se,to,_rt = hash[mp(_l,_r)],_op = op[mp(_l,_r)];
	//_l   l   r  _r -> [_l,l-1] [l,_r]
	if(_op == 1)  split1(to,_rt,l-_l); else split2(to,_rt,l-_l);
	hash[mp(_l,l-1)] = to;   op[mp(_l,l-1)] = _op;
	hash[mp(l,_r)  ] = _rt;  op[mp(l,_r)  ] = _op;
	hash.erase(mp(_l,_r));   op.erase(mp(_l,_r));
	h.erase(h.lower_bound(mp(_l,_r))); h.insert(mp(_l,l-1)); h.insert(mp(l,_r));
	it = h.lower_bound(mp(l,_r));
	return it;
}
int work(int k,int l,int r)
{
	int tmp = tree::New();
	while(1)
	{
		it = h.lower_bound(mp(l,0));
		if(it == h.end() || it->second > r) break;
		tmp = tree::merge(tmp,hash[(*it)]);
		hash.erase(mp(it->first,it->second));
		op.erase(mp(it->fr,it->se));
		h.erase(it);
    }//合并所有子區間 
    it = h.lower_bound(mp(l,0));
    if((it == h.end() || it->first > r) && it != h.begin())
    {
    	it--;
	    if(it->first < l && it->second > r) it = fuckit(it,l,r);
        else it++;
	}
    if(it != h.end() && it->first <= r)
    {
    	int to;
    	int _op = op[mp(it->fr,it->se)],_l = it->fr,_r = it->se,_rt = hash[mp(_l,_r)];
    	if(_op == 1) split1(to,_rt,r-_l+1);
    	else split2(to,_rt,r-_l+1);
    	hash[mp(r+1,_r)] = _rt;  op[mp(r+1,_r)] = _op;
		op.erase(mp(_l,_r));     hash.erase(mp(_l,_r));
        h.insert(mp(r+1,_r));    h.erase(it);
        tmp = tree::merge(tmp,to);
	}//處理右端區間
    it = h.lower_bound(mp(l,0));
    if(it == h.begin());
    else
    {
    	it--;
    	if(it->second >= l)
    	{
    	    if(it->second > r) it = fuckit(it,l,r);
    		int to;
    		int _l = it->fr,_r = it->se,_op = op[mp(_l,_r)],_rt = hash[mp(_l,_r)];
    		if(_op == 1) split2(to,_rt,_r-l+1);
    		else split1(to,_rt,_r-l+1);
    		hash[mp(_l,l-1)] = _rt;  op[mp(_l,l-1)] = _op;
    		op.erase(mp(_l,_r));     hash.erase(mp(_l,_r));
    		h.insert(mp(_l,l-1));    h.erase(it);
    		tmp = tree::merge(tmp,to);
    	}
    }//處理左端區間
    h.insert(mp(l,r)); op[mp(l,r)] = k; hash[mp(l,r)] = tmp;
}
void init()
{
	n = rd();m = rd();
	rep(i,1,n) a[i] = rd();
	rep(i,1,n) h.insert(mp(i,i)),tree::build(hash[mp(i,i)],1,n,a[i]);
	rep(i,1,m)
	{
		int k = rd(),l = rd(),r = rd();
		work(k,l,r);
	}
	int pl = rd();
	for(it = h.begin();it != h.end();it++) if(pl >= it->fr && pl <= it->se) break;
	int _rt = hash[mp(it->fr,it->se)],_op = op[mp(it->fr,it->se)],l = it->fr,r = it->se;
	printf("%d\n",tree::query(hash[mp(it->fr,it->se)],1,n, _op?r-pl+1:pl-l+1 ));
}
int main()
{
	init();
	return 0;
}
           

做法②:

直接對數字排序不好維護。

對01的排序非常好維護

考慮二分

二分一個答案 m i d mid mid後把 ≥ m i d \geq mid ≥mid的值設為1, &lt; m i d &lt;mid <mid的值設為0,剩下維護的操作就隻涉及區間修改,區間和查詢了

複雜度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代碼簡短

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define ls rt<<1
#define rs rt<<1|1
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
int n,m;
int a[101000],b[101000];
int num[401000],det[401000];
struct data{int op,l,r;}q[101000];
void pushdown(int rt,int l,int r)
{
	int mid = l+r>>1;
	if(det[rt] == 1)
		num[ls] = mid-l+1,
		num[rs] = r-mid,
		det[ls] = det[rs] = 1,
		det[rt] = 0;
	else if(det[rt] == 2) num[ls] = num[rs] = 0,det[ls] = det[rs] = 2,det[rt] = 0;
}
void build(int rt,int l,int r)
{
	if(l == r)
	{
		num[rt] = b[l];
		return;
	}
	int mid = l+r>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	det[rt] = 0;num[rt] = num[ls] + num[rs];
}
int query(int rt,int l,int r,int x,int y)
{
	if(y < l || x > r) return 0;
	if(x <= l && r <= y) return num[rt];
	int mid = l+r>>1;
	pushdown(rt,l,r);
	return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
}
void change(int rt,int l,int r,int x,int y,int v)
{
	if(x <= l && r <= y)
	{
		det[rt] = 2-v;
		num[rt] = (r-l+1) * v;
		return;
	}
	pushdown(rt,l,r);
	int mid = l+r>>1;
	if(mid >= x) change(ls,l,mid,x,y,v);
	if(mid <  y) change(rs,mid+1,r,x,y,v);
	num[rt] = num[ls] + num[rs];
}
void work()
{
	build(1,1,n);
	rep(i,1,m)
	{
		int op = q[i].op,l = q[i].l,r = q[i].r;
		int num1 = query(1,1,n,l,r);
		if(op == 1)
		{
			if(num1>0)    change(1,1,n,l,l+num1-1,1);
			if(l+num1<=r) change(1,1,n,l+num1,r,0);
		}
		else
		{
			if(r-num1>=l) change(1,1,n,l,r-num1,0);
			if(num1>0)    change(1,1,n,r-num1+1,r,1);
		}
	}
}
int main()
{
	n = rd();m = rd();
	rep(i,1,n) a[i] = rd();
	rep(i,1,m) q[i].op = rd(),q[i].l = rd(),q[i].r = rd();
	int k = rd();
	int l = 0,r = n+1;
	while(l+1<r)
	{
		int mid = l+r>>1;
		rep(i,1,n) b[i] = a[i] > mid;
		work();
		if(query(1,1,n,k,k) == 1) l = mid;
		else r = mid;
	}
	printf("%d\n",r);
	return 0;
}