zoukankan      html  css  js  c++  java
  • 分块

    分块

    大家好,我非常喜欢暴力数据结构

    就是把数组分成 \(\lceil \sqrt n \rceil\) 块,每一块的长度最大是 \(\lfloor \sqrt n \rfloor\)

    左端点是 \(L_i\) ,右端点是 \(R_i\)\(i\) 所在的块是 \(id_i\)

    对于一个区间查询 \([l,r]\),分成三个部分

    首先 \(p=id_l,q=id_r\)

    • \(p=q\) 直接暴力
    • \(p\ne q\)
      1. \([l,R_p]\) 暴力处理
      2. \([L_q,r]\) 暴力处理
      3. \((p,q)\) 块,用懒标记或二分

    时间复杂度 \(\sqrt n\)

    使用范围: \(n<10^6\) ,或者代替一些高级的大常数数据结构

    代替线段树

    例:POJ - 3468:区间加,区间求和

    边角暴力,整块打上懒标记即可

    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int N=100005;
    typedef long long LL;
    int n,T,L[N],R[N],pos[N],tt;
    LL x[N],tg[N],sm[N];
    char opt[5];
    inline void Change(int l,int r,LL vl) {
    	register int p=pos[l],q=pos[r];
    	if(p==q) {
    		for(int i=l;i<=r;i++)x[i]+=vl;
    		sm[p]+=vl*(r-l+1);
    		return;
    	}
    	for(int i=p+1;i<=q-1;i++)tg[i]+=vl;
    	for(int i=l;i<=R[p];i++)x[i]+=vl;
    	sm[p]+=vl*(R[p]-l+1);
    	for(int i=L[q];i<=r;i++)x[i]+=vl;
    	sm[q]+=vl*(r-L[q]+1);
    }
    inline LL Ask(int l,int r) {
    	register int p=pos[l],q=pos[r];
    	register LL res=0;
    	if(p==q) {
    		for(int i=l;i<=r;i++)res+=x[i];
    		res+=tg[p]*(r-l+1);
    		return res;
    	}
    	for(int i=p+1;i<=q-1;i++)
    		res+=sm[i]+tg[i]*(R[i]-L[i]+1);
    	for(int i=l;i<=R[p];i++)res+=x[i];
    	res+=tg[p]*(R[p]-l+1);
    	for(int i=L[q];i<=r;i++)res+=x[i];
    	res+=tg[q]*(r-L[q]+1);
    	return res;
    }
    int main() {
    	scanf("%d%d",&n,&T);
    	for(int i=1;i<=n;i++)scanf("%lld",&x[i]);
    	tt=sqrt(1.0*n);
    	for(int i=1;i<=tt;i++) {
    		L[i]=(i-1)*tt+1;
    		R[i]=i*tt;
    	}
    	if(R[tt]<n)++tt,L[tt]=R[tt-1]+1,R[tt]=n;
    	for(int i=1;i<=tt;i++)
    		for(int j=L[i];j<=R[i];j++)
    			pos[j]=i,sm[i]+=x[j];
    	for(int l,r;T--;) {
    		LL v;
    		scanf("%s%d%d",opt,&l,&r);
    		if(opt[0]=='C')scanf("%lld",&v),Change(l,r,v);
    		else printf("%lld\n",Ask(l,r));
    	}
    }
    
    

    代替主席树

    例:Dynamic Rankings:带修主席树,查找区间第 \(k\)

    块内排序,每次修改也排

    二分答案,\(check\)\(mid\) 大的数的个数是否比 \(k\)

    至于查询:边角暴力,块内二分

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100005;
    int n,T,sq,x[N],y[N],id[N],L[N],R[N];
    char op[5];
    inline void bui(int p) {
    	for(int i=L[p];i<=R[p];i++)
    		y[i]=x[i];
    	sort(y+L[p],y+R[p]+1);
    }
    inline void mdy(int p,int v) {
    	x[p]=v,bui(id[p]);
    }
    inline int get(int p,int v) {
    	register int LL=L[p],RR=R[p];
    	register int mid,ans=-1;
    	while(LL<=RR) {
    		mid=LL+RR>>1;
    		if(y[mid]<=v)
    			ans=mid,LL=mid+1;
    		else RR=mid-1;
    	}
    	return ~ans?ans-L[p]+1:0;
    }
    inline int Ask(int l,int r,int k) {
    	register int LL=0,RR=1e9,mid,ans=1;
     	register int p=id[l],q=id[r],cnt;
    	while(LL<=RR) {
    		mid=LL+RR>>1,cnt=0;
    		if(p==q) {
    			for(int i=l;i<=r;i++)
    				if(x[i]<=mid)++cnt;
    		} else {
    			for(int i=l;i<=R[p];i++)
    				if(x[i]<=mid)++cnt;
    			for(int i=L[q];i<=r;i++)
    				if(x[i]<=mid)++cnt;
    			for(int i=p+1;i<q;i++)
    				cnt+=get(i,mid);			
    		}
    		if(cnt>=k)
    			ans=mid,RR=mid-1;
    		else LL=mid+1;
    	}
    	return ans;
    }
    int main() {
    	scanf("%d%d",&n,&T);
    	for(int i=1;i<=n;i++)scanf("%d",&x[i]);
    	sq=sqrt(n);
    	for(int i=1;i<=sq;i++)L[i]=R[i-1]+1,R[i]=i*sq;
    	if(R[sq]<n)++sq,L[sq]=R[sq-1]+1,R[sq]=n;
    	for(int i=1;i<=sq;i++) {
    		for(int j=L[i];j<=R[i];j++)
    			id[j]=i;
    		bui(i);
    	}
    	for(int l,r,k;T--;) {
    		scanf("%s",op);
    		if(op[0]=='C') {
    			scanf("%d%d",&l,&r);
    			mdy(l,r);
    		} else {
    			scanf("%d%d%d",&l,&r,&k);
    			printf("%d\n",Ask(l,r,k));
    		}
    	}
    }
    

    代替平衡树

    来一波刺激的:代替树套树

    二逼平衡树

    如果上面两题都没问题了,这题只需要时间

    排名、前驱后继都是块内二分,修改和第 \(K\) 大同上

    调了一个晚修。。。

    #include<bits/stdc++.h>
    #define fo(i,a,b) for(register int i=(a);i<=(b);i++)
    using namespace std;
    char buf[100000],*S=buf,*T=buf;
    inline char G() { return S==T&&(T=(S=buf)+fread(buf,1,100000,stdin),S==T)?EOF:*S++; }
    inline int Rd() {
    	register int x=0; char C=G();
    	for(;C<'0'||C>'9';C=G());
    	for(;C>'/'&&C<':';C=G())x=(x<<1)+(x<<3)+(C^48);
    	return x;
    }
    inline void Wr(int x) { if(x>9)Wr(x/10); putchar(x%10+48); }
    inline void Out(int x) { if(x<0)putchar(45),x=-x; Wr(x),putchar(10); }
    const int N=50005; int n,TT,x[N],y[N],id[N],L[N],R[N],sq,op,l,r,k;
    inline void bui(int p) { fo(i,L[p],R[p])y[i]=x[i]; sort(y+L[p],y+R[p]+1); }
    inline void mdy(int p,int v) { x[p]=v,bui(id[p]); }
    inline int low(int p,int v) {
    	register int LL=L[p],RR=R[p],mid,ans=L[p]-1;
    	while(LL<=RR) {
    		mid=LL+RR>>1;
    		if(y[mid]<v)ans=mid,LL=mid+1;
    		else RR=mid-1;
    	} return ans-L[p]+1;
    }
    inline int get(int p,int v) {
    	register int LL=L[p],RR=R[p],mid,ans=L[p]-1;
    	while(LL<=RR) {
    		mid=LL+RR>>1;
    		if(y[mid]<=v)ans=mid,LL=mid+1;
    		else RR=mid-1;
    	} return ans-L[p]+1;
    }
    inline int rnk() {
    	register int p=id[l],q=id[r],res=1;
    	if(p==q) {
    		fo(i,l,r)res+=(x[i]<k);
    		return res;
    	}
    	fo(i,l,R[p])res+=(x[i]<k);
    	fo(i,L[q],r)res+=(x[i]<k);
    	fo(i,p+1,q-1)res+=low(i,k);
    	return res;
    }
    inline int kth() {
    	register int LL=0,RR=1e8,mid,ans=1,p=id[l],q=id[r],cnt;
    	while(LL<=RR) {
    		mid=LL+RR>>1,cnt=0;
    		if(p==q) {
    			fo(i,l,r)cnt+=(x[i]<=mid);
    		} else {
    			fo(i,l,R[p])cnt+=(x[i]<=mid);
    			fo(i,L[q],r)cnt+=(x[i]<=mid);
    			fo(i,p+1,q-1)cnt+=get(i,mid);	
    		}
    		if(cnt>=k)ans=mid,RR=mid-1;
    		else LL=mid+1;
    	}
    	return ans;
    }
    inline int pre() {
    	register int p=id[l],q=id[r],ans=-2147483647,LL,RR,mid,res;
    	if(p==q) {
    		fo(i,l,r)if(x[i]<k)ans=max(ans,x[i]);
    		return ans;
    	}
    	fo(i,l,R[p])if(x[i]<k)ans=max(ans,x[i]);
    	fo(i,L[q],r)if(x[i]<k)ans=max(ans,x[i]);
    	fo(i,p+1,q-1) {
    		LL=L[i],RR=R[i],res=-1;
    		while(LL<=RR) {
    			mid=LL+RR>>1;
    			if(y[mid]<k)res=mid,LL=mid+1;
    			else RR=mid-1;
    		}
    		if(~res && y[res]<k)ans=max(ans,y[res]);
    	}
    	return ans;
    }
    inline int suc() {
    	register int p=id[l],q=id[r],ans=2147483647,LL,RR,mid,res;
    	if(p==q) {
    		fo(i,l,r)if(x[i]>k)ans=min(ans,x[i]);
    		return ans;
    	}
    	fo(i,l,R[p])if(x[i]>k)ans=min(ans,x[i]);
    	fo(i,L[q],r)if(x[i]>k)ans=min(ans,x[i]);
    	fo(i,p+1,q-1) {
    		LL=L[i],RR=R[i],res=-1;
    		while(LL<=RR) {
    			mid=LL+RR>>1;
    			if(y[mid]>k)res=mid,RR=mid-1;
    			else LL=mid+1;
    		}
    		if(~res && y[res]>k)ans=min(ans,y[res]);		
    	}
    	return ans;
    }
    int main() {
    	n=Rd(),TT=Rd(),sq=sqrt(n);
    	fo(i,1,n)x[i]=Rd();
    	fo(i,1,sq)L[i]=R[i-1]+1,R[i]=i*sq;
    	if(R[sq]<n)++sq,L[sq]=R[sq-1]+1,R[sq]=n;
    	fo(i,1,sq) {
    		fo(j,L[i],R[i])id[j]=i;
    		bui(i);
    	}
    	while(TT--) {
    		op=Rd(),l=Rd(),r=Rd();
    		if(op^3)k=Rd();
    		if(op==1)Out(rnk());
    		else if(op==2)Out(kth()); 
    		else if(op==3)mdy(l,r);
    		else if(op==4)Out(pre());
    		else if(op==5)Out(suc());
    	}
    } 
    

    总结

    分块虽然暴力,却是一个不错的数据结构

    此外,如果数据够小,一些规律题可以用上述方法打表

    这就是分块打表,复杂度 \(O(\sqrt n)\)

    更重要的是如何运用

  • 相关阅读:
    Mac电脑,Andorid studio 配置 Flutter
    java.lang.RuntimeException: com.intellij.ide.plugins.PluginManager
    #Java Web累积#关于MUI的上滑和下拉加载
    #iOS问题记录#WKWebView 闪退异常
    #iOS问题记录#UITableView加载后直接滑动倒最底部
    #Java Web累积#JS动态加载所有同name的select的option
    #iOS问题记录# UIWebView滑动到底部
    #Java Web累积#表格<table>中隐藏列做备用数据
    #iOS问题记录# UITextview富文本链接,禁止长按事件
    Day 1:自定义tableview cell xib版
  • 原文地址:https://www.cnblogs.com/KonjakLAF/p/14691338.html
Copyright © 2011-2022 走看看