3110: [Zjoi2013]K大數查詢
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 10240 Solved: 3053
[Submit][Status][Discuss]
Description
有N個位置,M個操作。操作有兩種,每次操作如果是1 a b c的形式表示在第a個位置到第b個位置,每個位置加入一個數c
如果是2 a b c形式,表示詢問從第a個位置到第b個位置,第C大的數是多少。
Input
第一行N,M
接下來M行,每行形如1 a b c或2 a b c
Output
輸出每個詢問的結果
Sample Input
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
2
1
HINT
【樣例說明】
第一個操作 後位置 1 的數只有 1 , 位置 2 的數也只有 1 。 第二個操作 後位置 1
的數有 1 、 2 ,位置 2 的數也有 1 、 2 。 第三次詢問 位置 1 到位置 1 第 2 大的數 是
1 。 第四次詢問 位置 1 到位置 1 第 1 大的數是 2 。 第五次詢問 位置 1 到位置 2 第 3
大的數是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
Source
發現整體二分居然如此好用?? 本來以爲要樹套樹的沒想到整體二分再用個樹狀數組就可以了... 好寫好調啊.
答案顯然具有二分性, 於是我們考慮嘗試一波整體二分. 我們對答案進行二分, 每次判斷一個詢問的區間比當前二分的值小的數目和該詢問的k作比較來判斷二分大了還是二分小了. 而修改操作相當於就是區間加減... 這個用線段樹可以維護. 然後就是普通的整體二分辣.
從別人博客裏發現這道題有負數的情況可以n - c + 1來擴展成1~2n+1的值域. 然後第k大就變成第k小了, 妙哇妙哇. 只需要在輸出的時候再減回來就可以了. 而且樹狀數組只需要再維護一個差分數組就可以資瓷區間加區間查詢?? 新技能get.
#include<bits/stdc++.h>
using namespace std;
typedef long long lnt;
const int maxn = 1e5 + 5;
bool vis[maxn];
lnt c[2][maxn];
int n, m, tot, lim, ans[maxn];
inline const int read() {
register int x = 0, f = 1;
register char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return f * x;
}
struct point {
int x, y, k, id, opt;
}a[maxn], tmp[maxn];
inline void mdy(int k, int x, int val) {
for (; x <= lim; x += x & -x) c[k][x] += val;
}
inline void modify(int x, int y, int f) {
mdy(0, x, f), mdy(1, x, f * (x - 1)), mdy(0, y + 1, -f), mdy(1, y + 1, -f * y);
}
inline lnt qry(int k, int x) {
lnt tmp = 0;
for (; x; x -= x & -x) tmp += c[k][x];
return tmp;
}
inline lnt query(int x, int y) {
return qry(0, y) * y - qry(1, y) - qry(0, x - 1) * (x - 1) + qry(1, x - 1);
}
void solve(int lf, int rg, int L, int R) {
register int i;
if (L == R) {
for (i = lf; i <= rg; ++ i)
if (a[i].opt) ans[a[i].id] = L;
return;
}
lnt sum = 0;
int mid = (L + R) >> 1, cnt = 0;
for (i = lf; i <= rg; ++ i) {
if (a[i].opt) {
sum = query(a[i].x, a[i].y);
if (sum < a[i].k) vis[i] = false, a[i].k -= (int)sum;
else vis[i] = true, ++ cnt;
} else if (a[i].k <= mid){
modify(a[i].x, a[i].y, +1), vis[i] = true;
cnt ++;
} else vis[i] = false;
}
for (i = lf; i <= rg; ++ i)
if (!a[i].opt && a[i].k <= mid) modify(a[i].x, a[i].y, -1);
int l1 = lf, l2 = lf + cnt;
for (i = lf; i <= rg; ++ i)
(vis[i]) ? tmp[l1 ++] = a[i] : tmp[l2 ++] = a[i];
for (i = lf; i <= rg; ++ i) a[i] = tmp[i];
solve(lf, l1 - 1, L, mid), solve(l1, l2 - 1, mid + 1, R);
}
int main() {
n = read(), m = read();
register int i;
for (i = 1; i <= m; ++ i) {
a[i].opt = read() - 1, a[i].x = read(), a[i].y = read(), a[i].k = read();
if (a[i].opt) a[i].id = ++ tot;
else a[i].k = n - a[i].k + 1, lim = max(lim, a[i].k);
}
solve(1, m, 1, lim);
for (i = 1; i <= tot; ++ i) printf("%d\n", n - ans[i] + 1);
return 0;
}