poj 2104 K-th Number 區間第K大 二分 離散化 + (莫隊 樹狀陣列/平方分解/線段樹)
題目
題解
比較經典的題目,我用了三個方法來寫。
其中第一種第三種我覺得比較好寫,第二種出現了各種問題。
總的來說第一種速度快,但是程式碼長,第三種速度慢一些,但是程式碼比較短,第二種程式碼和第三種差不多,但是慢了很多,寫起來很蛋疼。
第一種是我自己寫的寫法,後兩種是書上的寫法。好像還可以用其他亂七八糟樹來弄。
題解1
首先可以離散化一下資料。
然後莫隊去搞,然後用樹狀陣列來維護整個個數,再去二分答案。
整個程式碼比較長,不過寫起來還是比較方便的。
莫隊+樹狀陣列程式碼
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
// head
const int N = 1e5+5;
const int M = 5e3+5;
const int B = 1000;
struct BIT {
#define T int
T tree[N] ;
inline int lowbit(int x) {
return x&(-x);
}
void add(int x, T add, int n) {
for (int i = x; i <= n; i += lowbit(i)) {
tree[i] += add;
}
}
T sum(int x) {
T ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
ans += tree[i];
}
return ans;
}
T query(int k, int n) {
int l = 1, r = n, ans = n;
while (l <= r) {
int mid = (l + r) / 2;
if (sum(mid) >= k) {
ans = mid;
r = mid-1;
} else {
l = mid+1;
}
}
return ans;
}
void clear(int n) {
for (int i = 1; i <= n; i++) {
tree[i] = 0;
}
}
#undef T
};
BIT bt;
struct Query {
int l, r, k, id;
};
bool cmp(const Query &a, const Query &b) {
int apos = a.l / B, bpos = b.l / B;
return apos == bpos ? a.r < b.r : apos < bpos;
}
Query query[M];
int a[N];
int dis[N];
int res[M];
void add(int x, int n) {
bt.add(x, 1, n);
}
void rem(int x, int n) {
bt.add(x, -1, n);
}
int main() {
int n, m;
while (scanf("%d%d", &n, &m) == 2) {
for (int i = 1; i <= n; i++) {
scanf("%d", a+i);
dis[i-1] = a[i];
}
sort(dis, dis + n);
int tot = unique(dis, dis + n) - dis;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(dis, dis + tot, a[i]) - dis + 1;
}
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &query[i].l, &query[i].r, &query[i].k);
query[i].id = i;
}
sort(query, query + m, cmp);
int l = 1, r = 0;
for (int i = 0; i < m; i++) {
while (r < query[i].r) add(a[++r], tot);
while (l > query[i].l) add(a[--l], tot);
while (r > query[i].r) rem(a[r--], tot);
while (l < query[i].l) rem(a[l++], tot);
res[query[i].id] = bt.query(query[i].k, tot);
}
for (int i = 0; i < m; i++) {
printf("%d\n", dis[res[i]-1]);
}
bt.clear(tot);
}
return 0;
}
題解2
然後我們也可以把整個東西劃分成約
n−−√ 個塊,每塊內排序。對於完整在裡面的一塊,可以二分,兩邊的則直接暴力for。
需要注意的是右邊的區間應該繼續往下減而不是停留在模
B 為0 。否則最後一塊的第一個元素會漏處理,可以處理成左閉右開。
平方分解程式碼
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
// head
const int N = 1e5+5;
const int B = 1000;
vector<int> b[N/B + 5];
int a[N], c[N];
int get(int l, int r, int x) {
int ans = 0;
while (l < r && l % B) {
if (a[l++] <= x) ans++;
}
while (l < r && r % B) {
if (a[--r] <= x) ans++;
}
for (int i = l; i < r; i += B) {
int pos = i / B;
ans += upper_bound(b[pos].begin(), b[pos].end(), x) - b[pos].begin();
}
return ans;
}
int query(int L, int R, int k, int n) {
int l = 0, r = n-1, ans;
while (l <= r) {
int mid = (l + r) / 2;
int cur = get(L, R, c[mid]);
if (cur >= k) {
ans = mid;
r = mid-1;
} else {
l = mid+1;
}
}
return c[ans];
}
int main() {
int n, m, x, l, r, k;
while (scanf("%d%d", &n, &m) == 2) {
for (int i = 0; i < n; i++) {
scanf("%d", a+i);
c[i] = a[i];
b[i/B].push_back(a[i]);
}
sort(c, c + n);
for (int i = 0; i <= n/B; i++) {
sort(b[i].begin(), b[i].end());
}
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &l, &r, &k);
l--;
printf("%d\n", query(l, r, k, n));
}
for (int i = 0; i <= n/B; i++) {
b[i].clear();
}
}
return 0;
}
題解3
然後可以建一顆歸併樹,在樹上找到區間然後去二分。
具體就是每個節點儲存一個升序的陣列,然後不斷merge。
線段樹程式碼
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
// head
const int N = 1e5+5;
int a[N];
struct SegmentTree {
#define lson (rt<<1)
#define rson ((rt<<1)|1)
#define MID ((L+R)>>1)
#define lsonPara lson, L, MID
#define rsonPara rson, MID+1, R
#define all(v) v.begin(), v.end()
const static int TN = N << 2;
vector<int> t[TN];
void pushUp(int rt, int L, int R) {
t[rt].resize(R - L + 1);
merge(all(t[lson]), all(t[rson]), t[rt].begin());
}
void build(int rt, int L, int R) {
if (L == R) {
t[rt].resize(1);
scanf("%d", a+L);
t[rt][0] = a[L];
} else {
build(lsonPara);
build(rsonPara);
pushUp(rt, L, R);
}
}
int query(int rt, int L, int R, int l, int r, int x) {
if (l > R || r < L || l > r) return 0;
if (l <= L && r >= R) {
int ans = upper_bound(all(t[rt]), x) - t[rt].begin();
return ans;
}
return query(lsonPara, l, r, x) + query(rsonPara, l, r, x);
}
};
SegmentTree st;
int main() {
int n, m, L, R, k;
while (scanf("%d%d", &n, &m) == 2) {
st.build(1, 1, n);
sort(a + 1, a + n + 1);
int tot = unique(a + 1, a + n + 1) - a;
while (m--) {
scanf("%d%d%d", &L, &R, &k);
int l = 1, r = tot-1, ans;
while (l <= r) {
int mid = (l + r) / 2;
if (st.query(1, 1, n, L, R, a[mid]) >= k) {
ans = mid;
r = mid-1;
} else {
l = mid+1;
}
}
printf("%d\n", a[ans]);
}
}
return 0;
}
相關推薦
poj 2104 K-th Number 區間第K大 二分 離散化 + (莫隊 樹狀陣列/平方分解/線段樹)
題目 題解 比較經典的題目,我用了三個方法來寫。 其中第一種第三種我覺得比較好寫,第二種出現了各種問題。 總的來說第一種速度快,但是程式碼長,第三種速度慢一些,但是程式碼比較短,第二種程式碼和第三種差不多,但是慢了很多,寫起
POJ 2104 K-th Number(區間第k大數)(平方切割,歸並樹,劃分樹)
ac代碼 deb rank turn tracking line 查看 div 能夠 題目鏈接: http://poj.org/problem?id=2104 解題思路: 由於查詢的個數m非常大。樸素的求法無法在規定時間內求解。因此應該選用合理的方式維護數據來做到高效
POJ 2985 The k-th Largest Group 第k大數 Treap / 樹狀陣列 + 並查集
題目連結 題意 有 n 只貓,m 次操作(n,m≤2e5): 0ij:將第 i 只貓所在組與第 j 只貓所在組合並; 1k:詢問第 k 大的組中有多少隻貓。 法一:Treap 參考資料 注意點 無需首先將所有組全部插入。可以在 treap
6231 K-th Number (2017CCPC哈爾濱站 二分+尺取法)
Alice are given an array A[1..N] with N numbers. Now Alice want to build an array B by a parameter K as following rules: Initially, the array B is empty
POJ 2104 K-th Number(主席樹,區間第K大的數)
Description You are working for Macrohard company in data structures department. After failing your previous task about key insertion you
POJ 2104 K-th Number 主席樹(求區間第k大)
主席書資料 題意:給出n個數,m次詢問,[x,y]內第k小的數時多少?n<=1e5,m<=5000 主席樹:對原序列的每個字首i都建立一個線段樹 維護值域[l,r]中的每個數,在字首i的
POJ 2104 K-th Number ( 求取區間 K 大值 )
二分法 esp size 麻煩 == 平方分割 closed push_back ret 題意 : 給出一個含有 N 個數的序列,然後有 M 次問詢,每次問詢包含 ( L, R, K ) 要求你給出 L 到 R 這個區間的第 K 大是幾 分析 : 求取區間 K 大值是
A - K-th Number POJ - 2104 -主席樹第一彈-第K大的數
感謝:http://www.cnblogs.com/zyf0163/p/4749042.html https://blog.csdn.net/qq_24451605/article/details/49031123 裸題多次查詢給定區間L—R內第K大的數 #include&
POJ 2104 K-th Number(主席樹)
ber sca first n) 次數 example == scan sorted K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 5742
POJ 2104 K-th Number (主席樹)
std +++ esp space ctype == string uniq upd 題意:給定一個序列,然後有 q 個詢問,每次詢問 l - r 區間內的第 k 大的值。 析:很明顯的主席樹,而且還是裸的主席樹,先進行離散化,然後用主席樹進行查詢就好。 代碼如下: #p
[POJ 2104]K-th Number
n) 劃分樹 tput lease lap form 我們 歸並 nts Description You are working for Macrohard company in data structures department. After failing your
[poj 2104]主席樹+靜態區間第k大
include end 區間 得到 name int 題目 tar tdi 題目鏈接:http://poj.org/problem?id=2104 主席樹入門題目,主席樹其實就是可持久化權值線段樹,rt[i]維護了前i個數中第i大(小)的數出現次數的信息,通過查詢兩棵樹的差
POJ 2104 K-th Number
poj 2104 working lan 只需要 please lin absolut input nlogn Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 5948
poj 2104主席樹求區間第k小
區間 ++ cto ast http lan air algorithm while POJ - 2104 題意:求區間第k小 思路:無修改主席樹 AC代碼: #include "iostream" #include "iomanip" #include "string.
Poj 2104 K-th Number 主席樹模版題
OS tdi pda sig signed begin ostream air color 題意:離線詢問[l,r]區間第k大 題解:模版題,入門題 #include <iostream> #include <cstdio> #inclu
POJ-2104 K-th Number CDQ分治
col 分享圖片 pri 找到 oid play out else mes 題目傳送門 題意:給你一個序列,長度為n,m次詢問,詢問一段區間的第k大。 題解:CDQ分治,對整個值域進行分治。每次取一個mid, 計算出整個區間內mid <= 的數目,如果 num >
【POJ 2104】【主席樹模板題】K-th Number
題意: 靜態詢問區間第K大問題。給出一個數組,然後多次詢問某一區間第K大數是多少。 思路: 典型的主席樹模板題。 所以就大致講一下靜態主席樹
K-th Number POJ - 2104 (主席樹 學習詳解)
https://cn.vjudge.net/problem/POJ-2104 題意 給你N個數 嗎、M次查詢,每次查詢給你 IJK 問第I個數到第J個數中第K大 思路 字典樹,每新增一個數都建立一棵線段樹,J和I 做減法就可以的到這個區間的線段樹 #include <c
歸併樹(POJ 2104 K-th Number)
在求解區間第k個數的問題,除了劃分樹以外我們還可以使用另一種高效的方法 ------ 歸併樹。 1、演算法描述 所謂歸併樹,就是利用線段樹的建樹過程,將歸併排序的過程儲存。(不會線段樹:here,不會歸併排序:here)。在說明歸併樹之前我們
poj 2104 K-th Number (主席樹入門模板題)
摘抄了一段主席樹的解釋:所謂主席樹呢,就是對原來的數列[1..n]的每一個字首[1..i](1≤i≤n)建立一棵線段樹,線段樹的每一個節點存某個字首[1..i]中屬於區間[L..R]的數一共有多少個(比如根節點是[1..n],一共i個數,sum[root]