1. 程式人生 > >bzoj4552(線段樹合併+stl||二分+線段樹)

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

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

做法①:
想法比較暴力
初始對每個點開一棵權值線段樹
每次詢問的時候,把 [ l , r ] [l,r]

兩端的塊分裂,再把區間內所有的線段樹合併
最後暴力查詢一下,用 s e t set 維護所有的塊

複雜度?
可以發現,我們新建的塊是 O ( n ) O(n)
那麼我們合併以及分裂的操作涉及的塊不難發現也是 O ( n ) O(n)
總複雜度 O ( n l o g n ) O(nlogn)

分裂的時候還要分塊的種類,本人使用了 m a p 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
            
           

相關推薦

bzoj4552線段合併+stl||二分+線段

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

字串(tjoi2016,heoi2016,bzoj4556)(sam(字尾自動機)+線段合併+倍增+二分答案)

佳媛姐姐過生日的時候,她的小夥伴從某東上買了一個生日禮物。生日禮物放在一個神奇的箱子中。箱子外邊寫了 一個長為\(n\)的字串\(s\),和\(m\)個問題。佳媛姐姐必須正確回答這\(m\)個問題,才能開啟箱子拿到禮物,升職加薪,出任CE O,嫁給高富帥,走上人生巔峰。每個問題均有\(a,b,c,d\)四個引

十七 結構,二分搜尋

為什麼要研究樹結構? 樹結構並不抽象,例如家譜,資料夾等等 優點: 高效     何為二叉樹? 和連結串列一樣,是動態資料結構,是天然遞迴結構(每個結點的左子樹也是二叉樹),但是是非線性的 二叉樹具有唯一根節點,每個結點最多隻能分兩個叉,每個結點最多有兩個孩子,每

【bzoj1143 CTSC2008】祭祀river最大獨立集--二分圖匹配

題目: 我是超連結 題解: 尋找一個最大點集A,滿足∀u,v∈V,(u,v)∉E  二分圖的最大獨立集=頂點數-最大匹配  程式碼: #include <cstdio> #include

The Suspects 解決多棵合併成一棵的問題

The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number o

bzoj2216 [Poi2011]Lightning Conductor決策單調性+分治/二分+單調棧

化簡一下就是求ans[i]=max{aj+|i−j|−−−−−√}−a[i]ans[i]=max{aj+|i−j|}−a[i] 我們把絕對值去掉,正著倒著各做一遍即可。 現在只考慮<i<i的jj 設k1<k2<ik1<k2&l

bzoj4552: [Tjoi2016&Heoi2016]排序二分+線段

pre algo 說明 size getchar() lib pla using 如何   又是久違的1A哇...   好喵喵的題!二分a[p],把大於mid的數改為1,小於等於mid的數改為0,變成01串後就可以用線段樹進行那一連串排序了,排序後如果p的位置上的數為0,

HDU 5649 DZY Loves Sorting二分答案+線段線段合併+線段分割

題意 一個 \(1\) 到 \(n\) 的全排列,\(m\) 種操作,每次將一段區間 \([l,r]\) 按升序或降序排列,求 \(m\) 次操作後的第 \(k\) 位。 \(1 \leq n \leq 10^5\) 思路 兩個 \(\log\) 的做法展現了二分答案的強大功能。首先二分列舉第 \(k

BZOJ4552 HEOI2016/TJOI2016排序線段合併+線段分裂

  很久以前寫過二分答案離線的做法,比較好理解。事實上這還是一個線段樹合併+分裂的板子題,相比離線做法以更優的複雜度做了更多的事情。具體不說了。怎麼交了一遍luogu上就跑第一了啊 #include<iostream> #include<cstdio> #include<

BZOJ4556:[Tjoi2016&Heoi2016]字串 字尾自動機+樹上倍增+二分答案+線段合併

題目分析:我發現我對線段樹合併一無所知QAQ。 先講一種簡單的做法:我們可以將字尾陣列建出來,對於每個詢問二分一個答案mid。然後從Rank[c]往上下兩個方向跳,找到一個區間[L,R],使得這個區間的字尾和c開頭的字尾的LCP大於等於mid。那麼如果

計蒜客16492 building二分線段/分塊

sin cst include sqrt ++ building scanf mat math 題解: 考慮用線段樹維護樓的最大值,然後這個問題就很簡單了。 每次可以向左二分出比x高的第一個樓a,同理也可以向右二分出另一個樓b,如果a,b都存在,答案就是b-a-1。 註意到

【BZOJ4025】二分線段分治,並查集

math namespace struct modify push clas str clu php 【BZOJ4025】二分圖(線段樹分治,並查集) 題面 BZOJ 題解 是一個二分圖,等價於不存在奇環。 那麽直接線段樹分治,用並查集維護到達根節點的距離,只計算就好了。

【題解】[牛客網NOIP賽前集訓營-提高組第一場]C.保護 LCA+線段動態開點+線段合併

題目連結 ___ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2e5+10; int n,m,hd[N],to

[LOJ#2473][九省聯考2018]祕密襲擊樹形DP+生成函式+線段合併+拉格朗日插值

Address 洛谷P4365 BZOJ5250 LOJ#2473 The First Step - 轉化 簡版題意:給定一棵點帶權樹,求樹上所有大小大於 k

BZOJ:5457: 城市(線段合併)尚待優化

5457: 城市 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 18  Solved: 12[Submit][Status][Discuss] Description 有n

Gym - 101194G Pandaria 並查集+倍增+線段合併

  題意: 給定一個無向圖。每個點有一種顏色。現在給定q個詢問,每次詢問x和w,求所有能通過邊權值不超過w的邊走到x的點的集合中,哪一種顏色的點出現的次數最多。次數相同時輸出編號最小的那個顏色。強制線上。   解題思路:膜拜大神們的程式碼!看了好久,終於

[BZOJ2212][Poi2011]Tree Rotations線段合併

Address 洛谷 P3521 BZOJ 2212 LOJ #2163 Solution 非常有意思的題 一個直觀的想法 對於一個點 u

【BZOJ4025】二分可撤銷並查集+線段分治

題目: BZOJ4025 分析: 定理:一個圖是二分圖的充要條件是不存在奇環。 先考慮一個弱化的問題:保證所有邊出現的時間段不會交叉,只會包含或相離。 還是不會?再考慮一個更弱化的問題:邊只會出現不會消失。 當加邊的時候,若\((u,v)\)不連通:一定不會構成奇環,將它加入。 若\(

BZOJ4881 線段遊戲二分圖+狀陣列/動態規劃+線段

  相當於將線段劃分成兩個集合使集合內線段不相交,並且可以發現線段相交等價於逆序對。也即要將原序列劃分成兩個單增序列。由dilworth定理,如果存在長度>=3的單減子序列,無解,可以先判掉。   這個時候有兩種顯然的暴力。   將點集劃分成兩部分使內部無邊顯然就是二分圖,於是第一種暴力是在逆序對之

BZOJ4919 大根堆動態規劃+線段合併/treap+啟發式合併

  一個顯然的dp是設f[i][j]為i子樹內權值<=j時的答案,則f[i][j]=Σf[son][j],f[i][a[i]~n]++。這樣是可以線段樹合併的,將各兒子加起來然後打上加法標記即可。需要標記永久化。   另一種做法是考慮擴充套件經典的單調佇列優化LIS的做法,維護子樹內答案為k時最小的最