Dynamic Rankings

传送门

这道题是带修主席树的板子题。我们先来考虑一下主席树带修改最暴力的做法,就是暴力修改与之有关的所有权值线段树,这样的话单次操作的复杂度就是\(O(nlogn)\)的,总体的操作就是\(O(n^2logn)\)的,显然吃不消。

因为主席树其实应用的是前缀和的思想,我们考虑一下在最开始的时候,我们是怎么维护单点修改的前缀和的?使用树状数组。在普通的主席树里我们用的是差分的思想,但是现在我们不用差分了,我们用树状数组的思想(有人说叫套一个树状数组,但是其实我觉得没有实际套上……怎么说都行),使得根节点为\(root_x\)的主席树维护的是\(lowbit_x\)区间之内的权值,修改的时候像树状数组一样修改,查询的时候也一样查询,之后就和普通的主席树很相似了。

在实现上还是有很多细节的。修改和建树不难,因为这时不需要考虑继承上一个根结点的信息,直接修改就可以。不过在查询的时候,我们不能先进入树中之后再调用\(lowbit\)进行循环查询,因为进入树中之后我们就不知道下一步往哪走了。所以解决办法是一开始先把\(l-1\)\(r\)能访问到的区间都跑出来,把他们的根节点存在数组里,之后在询问的时候,同步的把所有根节点都向其左/右儿子移动就可以了,顺便使用这个来统计答案,确定应该往哪边走。

这样的话这道题修改是\(O(log^2n)\),总复杂度为\(O(nlog^2n)\),可以通过。

还有就是此题实际上不需要离散化,因为主席树上权值只是用来帮你判断应该往哪走,这题因为没要求强制在线,所以可以像我一样,先离线把所有的都存下来,之后进行离散化之后再操作,也可以不离散直接操作,但是这样二分的边界会大一点。

我自己试了一下,对于普通的主席树,其实离不离散对于答案的正确性影响不大,但是如果不离散,那么二分的范围必须开到上下界,也就是稳定的\(O(nlogmax_{val})\),但是离散化以后就会好很多。不离散的话会浪费时间和空间,不过其实也不会太多,一般会多几个常数的\(O(n)\)复杂度,差的不是很多。比如这题离不离散只差了1000ms(一共14000ms)。

所以离不离散的话可能ssy说的对:“你觉得不离散过不去的时候。”

看一下代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
const int M = 400005;
const int N = 30000005;
 
int read()
{
   int ans = 0,op = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9')
   {
      if(ch == '-') op = -1;
      ch = getchar();
   }
   while(ch >='0' && ch <= '9')
   {
      ans *= 10;
      ans += ch - '0';
      ch = getchar();
   }
   return ans * op;
}

struct node
{
   int ls,rs,v;
}t[N];

int a[M],b[M],n,m,x[M],y[M],z[M],c[M],idx,t1[M],t2[M],cnt1,cnt2,root[M],tot;
char s[2];

void modify(int &p,int l,int r,int pos,int val)
{
   if(!p) p = ++idx;
   t[p].v += val;
   if(l == r) return;
   int mid = (l+r) >> 1;
   if(pos <= mid) modify(t[p].ls,l,mid,pos,val);
   else modify(t[p].rs,mid+1,r,pos,val);
}

void change(int pos,int val)
{
   int x = pos;
   while(x <= n) modify(root[x],1,tot,a[pos],-1),x += lowbit(x);
   a[pos] = val,x = pos;
   while(x <= n) modify(root[x],1,tot,a[pos],1),x += lowbit(x);
}

int query(int l,int r,int k)
{
   if(l == r) return l;
   int sum = 0,mid = (l+r) >> 1;
   rep(i,1,cnt1) sum -= t[t[t1[i]].ls].v;
   rep(i,1,cnt2) sum += t[t[t2[i]].ls].v;
   if(k <= sum)
   {
      rep(i,1,cnt1) t1[i] = t[t1[i]].ls;
      rep(i,1,cnt2) t2[i] = t[t2[i]].ls;
      return query(l,mid,k);
   }
   else
   {
      rep(i,1,cnt1) t1[i] = t[t1[i]].rs;
      rep(i,1,cnt2) t2[i] = t[t2[i]].rs;
      return query(mid+1,r,k - sum);
   }
}

int main()
{
   n = read(),m = read();
   rep(i,1,n) a[i] = b[i] = read();
   rep(i,1,m)
   {
      scanf("%s",s);
      if(s[0] == 'Q') x[i] = read(),y[i] = read(),z[i] = read();
      else c[i] = read(),a[i+n] = b[i+n] = read();
   }
   sort(b+1,b+n+m+1);
   tot = unique(b+1,b+1+n+m) - b - 1;
   rep(i,1,n+m) a[i] = lower_bound(b+1,b+1+tot,a[i]) - b;
   rep(i,1,n)
   {
      int j = i;
      while(j <= n) modify(root[j],1,tot,a[i],1),j += lowbit(j);
   }
   rep(i,1,m)
   {
      if(!x[i]) {change(c[i],a[i+n]);continue;}
      int j = x[i] - 1,k = y[i];
      cnt1 = cnt2 = 0;
      while(j) t1[++cnt1] = root[j],j -= lowbit(j);
      while(k) t2[++cnt2] = root[k],k -= lowbit(k);
      printf("%d\n",b[query(1,tot,z[i])]);
   }
   return 0;
}

posted @ 2018-12-10 23:04  CaptainLi  阅读(210)  评论(0编辑  收藏  举报