题目:
http://acm.hdu.edu.cn/showproblem.php?pid=5412
题意:
给定一个长度为n的序列,有以下两种操作:1 x y k,求区间[x,y]中的第k小数,2 x y,把位置x的元素替换为y。
思路:
用的整体二分。谈一下自己对整体二分的理解(如有错误请谅解):整体二分总体来说是分治,然后用二分枚举一个值,在分治的时候用这个枚举值来划分左右子区间。像求第k小数,二分枚举一个值,对于序列中的元素,如果小于等于枚举值,就划分到左子区间,否则就划分到右子区间;对于询问,判断当前枚举值时,统计询问区间中元素在左子区间中出现的个数,如果大于等于k的话,答案肯定在左子区间中,把这个查询也划分到左子区间中,否则的话,划分到右子区间中,可以发现右子区间的枚举值肯定是比当前枚举值大,意味着左子区间中的元素对于右子区间中询问贡献是固定的,可以直接减去这个贡献,然后再在右子区间中查找。另外这个题有修改操作,可以把修改看做删除原位置上的数,然后在原位置上再插入一个数,分解为两步,按顺序加入到操作队列中,对于序列初始值,可以看做只插入不删除,然后按静态的做就好了,因为操作队列是按时间序排的,这样一定是正确的。
这个题与ZOJ2112基本是一样的,不过那道题数据范围要小很多,特别是查询少了很多!于是那个题我用分块水了过去,这道题用分块就TLE了,毕竟查询太多了。以后查询多的不能用分块,毕竟这玩意其实就是个优美的暴力,查询少的可以尝试一下
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100000 + 10, M = 100000 + 10;
struct BIT
{
int n, b[N];
void init(int _n)
{
n = _n;
memset(b, 0, sizeof b);
}
void add(int i, int x)
{
while(i <= n) b[i] += x, i += i & -i;
}
int sum(int i)
{
int ans = 0;
while(i > 0) ans += b[i], i -= i & -i;
return ans;
}
} bit;
struct node
{
int x, y, k, id, type;
void init(int _x, int _y, int _k, int _id, int _type)
{
x = _x, y = _y, k = _k, id = _id, type = _type;
}
} q[N+M*2], ql[N+M*2], qr[N+M*2];
int a[N], ans[M];
void divide_conquer(int st, int en, ll l, ll r)
{
if(st > en) return;
if(l == r)//此时答案确定
{
for(int i = st; i <= en; i++)
if(q[i].type == 2) ans[q[i].id] = l;
return;
}
ll mid = (l + r) >> 1;
int kl = 0, kr = 0;
for(int i = st; i <= en; i++)
{
if(q[i].type == 1)//序列中的元素
{
if(q[i].x <= mid)
{
ql[kl++] = q[i];
bit.add(q[i].id, q[i].y);//树状数组统计
}
else qr[kr++] = q[i];
}
else//查询操作
{
int num = bit.sum(q[i].y) - bit.sum(q[i].x - 1);
if(num >= q[i].k) ql[kl++] = q[i];
else
{
q[i].k -= num;
qr[kr++] = q[i];
}
}
}
for(int i = 0; i < kl; i++)//还原树状数组
if(ql[i].type == 1) bit.add(ql[i].id, -ql[i].y);
for(int i = 0; i < kl; i++) q[st+i] = ql[i];
for(int i = 0; i < kr; i++) q[st+kl+i] = qr[i];
divide_conquer(st, st + kl - 1, l, mid);
divide_conquer(st + kl, en, mid + 1, r);
}
int main()
{
int n, m;
while(~ scanf("%d", &n))
{
bit.init(n);
int tot = 0, x, y, k;
char ch;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
q[++tot].init(a[i], 1, 0, i, 1);
}
scanf("%d", &m);
for(int i = 1; i <= m; i++)
{
scanf(" %c%d%d", &ch, &x, &y);
if(ch == '2')
{
scanf("%d", &k);
q[++tot].init(x, y, k, i, 2);
}
else
{
q[++tot].init(a[x], -1, 0, x, 1);
q[++tot].init(y, 1, 0, x, 1);
a[x] = y;
}
}
memset(ans, -1, sizeof ans);
divide_conquer(1, tot, INT_MIN, INT_MAX);
for(int i = 1; i <= m; i++)
if(ans[i] != -1) printf("%d\n", ans[i]);
}
return 0;
}