您的位置:首页 > 其它

【ZJOI 2013】 K大数查询 · 续

2013-04-12 20:23 176 查看
· 在线算法弱爆了...

· 分治神器

· Orz小oy

小oy竟然BZOJ rank1了?!

这怎么行呢...~\(≧▽≦)/~

原来说过本题可以用树套树搞定...不过那是在线算法.

今天说说离线算法...

考虑到在线算法是在值域上做二分.

那么同样可以考虑离线二分.

定义solve(L, R), 表示解决答案值域在[L, R]中的询问.

明显我们需要求的是solve(1, n)

考虑调用solve(1, n), 我们可以将所有的操作分为2类:

对于1操作, 如果其修改值≤ n / 2, 归入类1

对于2操作, 如果其答案值域≤ n / 2, 归入类1

这样, 每层我们的任务都是将当前操作分类.

对于1操作, O(1) 可分类.

对于2操作, 我们可以转化为如下问题:

判断[l, r] 中≤ n / 2 的数的个数是否<k

其中l, r为2操作的区间, k为2操作的询问值.

明显, 我们已经将在该操作之前的所有修改值≤ n / 2 的1操作分类过.

只需要维护一个区间和即可.

分类完后递归询问solve(1, n / 2), solve(n / 2 + 1, n), 依次类推即可.

为了常数用树状数组吧.

于是复杂度O(nlog^2n), 常数小得可怕.

特别无聊把小oy压了4ms... ( 有意义么! 你这个大丧失! )

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

int n, m, max_i, set;
int vis1[50010], vis2[50010];
int right[20][50010], left[20][50010], tot1[50010], tot2[50010];
int a[50010], b[50010], c[50010], d[50010], now[50010], ans[50010];

int toT(int *q, int *p, int x)
{
int t = 0;
for (int i = x; i; i -= i & -i)
if (q[i] == set)  t += p[i];
else  q[i] = set, p[i] = 0;
return t;
}
void add(int *q, int *p, int x, int w)
{
for (int i = x; i <= max_i; i += i & -i)
if (q[i] == set) p[i] += w;
else  q[i] = set, p[i] = w;
}
void solve(int cr, int *p, int l, int r)
{
if (!p[0])  return;
int mid = (l + r) >> 1;
int *rig = right[cr], *lef = left[cr];
rig[0] = lef[0] = 0;
++set;
for (int i = 1; i <= p[0]; ++i)  {
int I = p[i];
if (a[I] == 1)
if (d[I] > mid)
rig[++rig[0]] = I;
else  {
add(vis1, tot1, b[I], 1);
add(vis2, tot2, b[I], b[I]);
add(vis1, tot1, c[I] + 1, -1);
add(vis2, tot2, c[I] + 1, -c[I] - 1);
lef[++lef[0]] = I;
}
else  {
int tot = toT(vis1, tot1, c[I]) * (c[I] + 1) - toT(vis2, tot2, c[I]) - toT(vis1, tot1, b[I] - 1) * b[I] + toT(vis2, tot2, b[I] - 1);
if (tot + now[I] < d[I])  now[I] += tot, ans[I] = mid, rig[++rig[0]] = I;
else  lef[++lef[0]] = I;
}
}

if (l == r)  return;
solve(cr + 1, lef, l, mid);
solve(cr + 1, rig, mid + 1, r);
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; ++i)  {
scanf("%d %d %d %d", a + i, b + i, c + i, d + i);
if (a[i] == 1)  d[i] = n - d[i] + 1;
right[0][i] = i;
}

right[0][0] = m;  max_i = n;
solve(1, right[0], 1, n);

for (int i = 1; i <= m; ++i)
if (a[i] == 2)
printf("%d\n", n - ans[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: