微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

【ZJOI 2013】 K大数查询 · 续

    · 在线算法弱爆了...

    · 分治神器

    · 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, 依次类推即可.

    为了常数用树状数组吧.

    于是复杂度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 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 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]);
        add(vis1,c[I] + 1,-1);
        add(vis2,-c[I] - 1);
        lef[++lef[0]] = I;
      }
    else  {
      int tot = toT(vis1,c[I]) * (c[I] + 1) - toT(vis2,c[I]) - toT(vis1,b[I] - 1) * b[I] + toT(vis2,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]);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐