【分块】【权值分块】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. RunID User Memory Time Language Code_Length Submit_Time
1 802901(8) lizitong 10192 KB 208 MS C++ 3468 B 2014-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;
}
时间: 2024-08-05 22:50:05

【分块】【权值分块】bzoj1901 Zju2112 Dynamic Rankings的相关文章

【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings

谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结点的权值线段树之间毫无关联 可以看这个:http://blog.csdn.net/popoqqq/article/details/40108669?utm_source=tuicool #include<cstdio> #include<algorithm> using namespa

BZOJ1901 Zju2112 Dynamic Rankings 【树状数组套主席树】

题目 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改 变后的a继续回答上面的问题. 输入格式 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000). 分别表示序列的长度和指令的个数. 第二行有n个数,表示a[1],a[2]--a[n],这些数都小于10^9. 接下来的m

【分块】bzoj1901 Zju2112 Dynamic Rankings

区间k大,分块大法好,每个区间内存储一个有序表. 二分答案,统计在区间内小于二分到的答案的值的个数,在每个整块内二分.零散的暴力即可. 还是说∵有二分操作,∴每个块的大小定为sqrt(n*log2(n))比较快呢. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 int n,a[10001],num[

bzoj1901(Zju2112 Dynamic Rankings)

题目链接:没有权限,进不去,题目也没法交,代码也不知道对不对,有好心人有权限帮忙交下吧QAQ 题目大意:带区间修改的第K小数 题目思路:有树套树,块状链表等优秀数据结构可以解决该题,当然我还是继续练习整体二分. 把修改操作拆开成两个操作 1.删除节点上的数 2.加入一个新数,然后更新的时候注意删除操作对树状数组的更新与插入操作相反,其他的没有难度 #include <iostream> #include <cstdio> #include <cstdlib> #incl

【莫队算法】【权值分块】bzoj3920 Yuuna的礼物

[算法一] 暴力. 可以通过第0.1号测试点. 预计得分:20分. [算法二] 经典问题:区间众数,数据范围也不是很大,因此我们可以: ①分块,离散化,预处理出: <1>前i块中x出现的次数(差分): <2>第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现 的次数,加上差分的结果,尝试更新ans. 时间复杂度O(m*sqrt(n)), 空间复杂度O(n*sqrt(n)). ②考虑离线,莫队算法,转移的时候使用数据

【权值分块】bzoj3570 DZY Loves Physics I

以下部分来自:http://www.cnblogs.com/zhuohan123/p/3726306.html DZY系列. 这题首先是几个性质: 1.所有球质量相同,碰撞直接交换速度,而球又没有编号,那么就可以直接视作两个球没有碰撞. 2.所有的方向.初始位置都没有任何用处. 然后就是速度的问题了,根据题设 a⋅v=C 与这几个方程联立 a⋅v=C s=v·t; vt2=v02+2·a·s 解这个方程组,可以得到 vt=√(2·C·t+v02) 那么T时刻的速度vT的相对大小就直接由v0决定了

【权值分块】bzoj1861 [Zjoi2006]Book 书架

权值分块……rank3……没什么好说的. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 int n,sz,sum,x,y,l[501],r[501],Min,Max,sumv[501],num[250001],m,v[250001],p[250001]; 6 bool b[250001]; 7 char op[6],c; 8 int Num,CH[1

【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network

裸题,直接上.复杂度O(n*sqrt(n)*log(n)). //Num[i]表示树中的点i在函数式权值分块中对应的点 //Map[i]表示函数式权值分块中的点i在树中对应的点 #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define N 80001 #define INF 2147483647 #define NN 87001 #define BN 296 int

【贪心】【二维偏序】【权值分块】bzoj1691 [Usaco2007 Dec]挑剔的美食家

既然题目中的要求满足二维偏序,那么我们很自然地想到将所有东西(草和牛)都读进来之后,对一维(美味度)排序,然后在另一维(价值)中取当前最小的. 于是,Splay.mutiset.权值分块什么的都支持查询后继呢. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 int Num,CH[12],f,c; 6 inline void R(int &x)