【函数式权值分块】【分块】bzoj1901 Zju2112 Dynamic Rankings

论某O(n*sqrt(n))的带修改区间k大值算法。

首先对序列分块,分成sqrt(n)块。

然后对权值分块,共维护sqrt(n)个权值分块,对于权值分块T[i],存储了序列分块的前i块的权值情况。

对于区间询问,需要获得区间中每个值出现的次数,然后按权值扫O(sqrt(n)),完整的部分我们可以通过权值分块差分(O(1))得到(比如Lb~Rb块就是T[Rb]-T[Lb-1]),零散的部分我们再维护一个额外的权值分块,累计上该值即可。O(sqrt(n))。

对于修改,直接在该位置之后的所有权值分块里修改,单次修改O(1),涉及O(sqrt(n))个权值分块,所以是O(sqrt(n))的。

所以平均每次操作是O(sqrt(n))的,空间复杂度是O(n*sqrt(n))的。

(缺陷:①必须离散化;②空间复杂度较高,对n=100000,几乎会卡空间)

这份代码目前在 bzoj 上 Rank1

No.RunIDUserMemoryTimeLanguageCode_LengthSubmit_Time
1802901(8)lizitong10192 KB208 MSC++3468 B2014-12-11 13:01:16
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int Num,CH[12],f,c;
inline void R(int &x){
    c=0;f=1;
    for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(x=0;c>='0'&&c<='9';c=getchar())(x*=10)+=(c-'0');
    x*=f;
}
inline void P(int x){
    if(x<10)putchar(x+'0');
    else{P(x/10);putchar(x%10+'0');}
}
struct Point{int v,p;}t[20001];
bool operator < (const Point &a,const Point &b){return a.v<b.v;}
int n,m,xs[10001],ys[10001],ks[10001],sum=1,en,en2,ma[20001],l[111],r[111];
int a[20001],num[10001],num2[20001],l2[145];
char op[10001];
struct Val_Block
{
	int b[20001],sumv[145];
	void Insert(const int &x){++b[x]; ++sumv[num2[x]];}
	void Delete(const int &x){--b[x]; --sumv[num2[x]];}
}T[111],S;
int Kth(const int &L,const int &R,const int &x)
{
	int cnt=0,res;
	if(num[L]+1>=num[R])
	  {
	  	for(int i=L;i<=R;++i) S.Insert(a[i]);
	  	for(int i=1;;++i)
          {
            cnt+=S.sumv[i];
            if(cnt>=x)
              {
                cnt-=S.sumv[i];
                for(int j=l2[i];;++j)
                {cnt+=S.b[j]; if(cnt>=x) {res=j; goto OUT2;}}
              }
          } OUT2:
        for(int i=L;i<=R;++i) S.Delete(a[i]);
        return res;
	  }
	for(int i=L;i<=r[num[L]];++i) S.Insert(a[i]);
	for(int i=l[num[R]];i<=R;++i) S.Insert(a[i]);
    int LB=num[L],RB=num[R]-1;
    for(int i=1;;++i)
      {
        cnt+=(T[RB].sumv[i]-T[LB].sumv[i]+S.sumv[i]);
        if(cnt>=x)
          {
            cnt-=(T[RB].sumv[i]-T[LB].sumv[i]+S.sumv[i]);
            for(int j=l2[i];;++j)
            {cnt+=(T[RB].b[j]-T[LB].b[j]+S.b[j]); if(cnt>=x) {res=j; goto OUT;}}
          }
      } OUT:
	for(int i=L;i<=r[num[L]];++i) S.Delete(a[i]);
	for(int i=l[num[R]];i<=R;++i) S.Delete(a[i]);
	return res;
}
void makeblock()
{
	int sz=sqrt(n); if(!sz) sz=1;
	for(;sum*sz<n;++sum)
	  {
	  	l[sum]=r[sum-1]+1; r[sum]=sum*sz;
	  	for(int i=l[sum];i<=r[sum];++i) num[i]=sum;
	  }
	l[sum]=r[sum-1]+1; r[sum]=n;
	for(int i=l[sum];i<=r[sum];++i) num[i]=sum;
}
void val_mb()
{
	int tot=1,sz=sqrt(en2); if(!sz) sz=1;
	for(;tot*sz<en2;++tot)
	  {
	  	l2[tot]=(tot-1)*sz+1;
	  	int R=tot*sz;
	  	for(int i=l2[tot];i<=R;++i) num2[i]=tot;
	  }
	l2[tot]=(tot-1)*sz+1;
	for(int i=l2[tot];i<=en2;++i) num2[i]=tot;
}
void Init_Ts()
{
	for(int i=1;i<=sum;++i)
	  {
	  	T[i]=T[i-1];
	  	for(int j=l[i];j<=r[i];++j) T[i].Insert(a[j]);
	  }
}
int main()
{
	R(n); R(m); en=n; makeblock();
	for(int i=1;i<=n;++i) {R(t[i].v); t[i].p=i;} getchar();
	for(int i=1;i<=m;++i)
	  {
	  	op[i]=getchar(); R(xs[i]); R(ys[i]);
	  	if(op[i]=='Q') R(ks[i]);
	  	else {t[++en].v=ys[i]; t[en].p=en;}
	  }
	sort(t+1,t+en+1);
	ma[a[t[1].p]=++en2]=t[1].v;
    for(int i=2;i<=en;++i)
      {
        if(t[i].v!=t[i-1].v) ++en2;
        ma[a[t[i].p]=en2]=t[i].v;
      }
	val_mb(); Init_Ts(); en=n;
    for(int i=1;i<=m;++i)
      {
      	if(op[i]=='C')
      	  {
      	  	++en;
      	  	for(int j=num[xs[i]];j<=sum;++j)
		      T[j].Delete(a[xs[i]]),T[j].Insert(a[en]);
		    a[xs[i]]=a[en];
      	  }
		else P(ma[Kth(xs[i],ys[i],ks[i])]),puts("");
      }
	return 0;
}

 

转载于:https://www.cnblogs.com/autsky-jadek/p/4157365.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值