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
:
這題有兩種做法,複雜度與思路都不同,但都是比較經典的做法。
做法①:
想法比較暴力
初始對每個點開一棵權值線段樹
每次詢問的時候,把
兩端的塊分裂,再把區間內所有的線段樹合併
最後暴力查詢一下,用
維護所有的塊
複雜度?
可以發現,我們新建的塊是
的
那麼我們合併以及分裂的操作涉及的塊不難發現也是
的
總複雜度
分裂的時候還要分塊的種類,本人使用了
程式碼細節較複雜。
#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時最小的最