【總結】 暴力資料結構——分塊
阿新 • • 發佈:2018-11-06
前言
分塊是一種優化暴力的方法,如果想要知道分塊有多麼神奇,詳情參考laofu的省選經歷。
本文所有例題可以在\(loj\)上面找到。
本文參考
註明
數列分塊入門 1 ~4較為簡單,觀看hzwer的部落格便可以看懂,所以不講解。
數列分塊入門 5
區間開方怎麼做?
我們考慮一下數最大不超過\(2^{31}-1\),而這個東西開個10次方就可以了。
所以直接判斷一下每一個塊是不是數都被開成了\(0\)或\(1\),所以就可以暴力一下就好了。
#include<stdio.h> #include<stdlib.h> #include<math.h> #define re register const int N=50010; int a[N],flag[N],bl[N],B,sum[N]; int min(int a,int b){ return a<b?a:b; } void Slove_sqrt(int i){ if(flag[i])return; flag[i]=1;sum[i]=0; for(int j=(i-1)*B+1;j<=i*B;j++){ a[j]=sqrt(a[j]); sum[i]+=a[j];if(a[j]>1)flag[i]=0; } } void update(int l,int r){ for(re int i=l;i<=min(r,bl[l]*B);i++){ sum[bl[l]]-=a[i]; a[i]=sqrt(a[i]); sum[bl[l]]+=a[i]; } if(bl[l]!=bl[r]) for(re int i=(bl[r]-1)*B+1;i<=r;i++){ sum[bl[r]]-=a[i]; a[i]=sqrt(a[i]); sum[bl[r]]+=a[i]; } for(re int i=bl[l]+1;i<=bl[r]-1;i++) Slove_sqrt(i); } int query(int l,int r){ int ans=0; for(re int i=l;i<=min(r,bl[l]*B);i++) ans+=a[i]; if(bl[l]!=bl[r]) for(re int i=(bl[r]-1)*B+1;i<=r;i++) ans+=a[i]; for(re int i=bl[l]+1;i<=bl[r]-1;i++) ans+=sum[i]; return ans; } int main(){ int n;scanf("%d",&n); B=sqrt(n); for(re int i=1;i<=n;i++)scanf("%d",&a[i]); for(re int i=1;i<=n;i++){ bl[i]=(i-1)/B+1; sum[bl[i]]+=a[i]; } while(n--){ int opt,l,r,c;scanf("%d%d%d%d",&opt,&l,&r,&c); if(!opt)update(l,r); else printf("%d\n",query(l,r)); } return 0; }
數列分塊入門 6
暫時沒有想到怎麼寫,可以存一下。。。
數列分塊入門 7
區間加法和區間乘法兩個肯定是有一個關係的,那麼我們考慮一下怎麼維護?
設原數\(x\),假設加了\(a\),乘了\(b\),那麼這個數現在是:
\(x*b+a\)
但是這個東西如果再乘一下應該外面的+號也要乘,加的話就只要加就好了.
#include<stdio.h> #include<stdlib.h> #include<math.h> #include<iostream> #define re register #define int long long using namespace std; const int N=100010,Mod=1e4+7; int a[N],atag[N],mtag[N],bl[N],B,n; void reset(int x){ for(re int i=(x-1)*B+1;i<=min(n,x*B);i++) a[i]=(a[i]*mtag[x]+atag[x])%Mod; atag[x]=0;mtag[x]=1; } void update(int f,int l,int r,int c){ reset(bl[l]); for(re int i=l;i<=min(r,bl[l]*B);i++) if(f)a[i]=(a[i]*c)%Mod; else a[i]=(a[i]+c)%Mod; if(bl[l]!=bl[r]){ reset(bl[r]); for(re int i=(bl[r]-1)*B+1;i<=r;i++) if(f)a[i]=(a[i]*c)%Mod; else a[i]=(a[i]+c)%Mod; } for(re int i=bl[l]+1;i<=bl[r]-1;i++){ if(f)(atag[i]*=c)%=Mod,(mtag[i]*=c)%=Mod; else (atag[i]+=c)%=Mod; } } signed main(){ scanf("%lld",&n);B=sqrt(n); for(re int i=1;i<=n;i++)scanf("%lld",&a[i]); for(re int i=1;i<=n;i++){ bl[i]=(i-1)/B+1;atag[bl[i]]=0;mtag[bl[i]]=1; } for(int T=1;T<=n;T++){ int opt,l,r,c;scanf("%lld%lld%lld%lld",&opt,&l,&r,&c); if(opt==2)printf("%lld\n",((a[r]*mtag[bl[r]]+atag[bl[r]])%Mod+Mod)%Mod); else update(opt,l,r,c); } return 0; }
數列分塊入門 8
很簡單啊,直接暴力改就好了(再加上一個標記判斷就行了啊!!!)
雖然我不會證明分塊的複雜度,但是一般性如果卡卡常,應該都是可行的。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<algorithm> #include<queue> #include<iostream> #include<map> #define ll long long #define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout); using namespace std; inline int gi(){ int sum=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();} return f*sum; } const int N=100010; int bl[N],a[N],tag[N],B,n; void reset(int x){ if(tag[x]==-1)return; for(int i=(x-1)*B+1;i<=min(n,x*B);i++) a[i]=tag[x]; tag[x]=-1; } int Slove(int l,int r,int c){ int ans=0; reset(bl[l]); for(int i=l;i<=min(r,bl[l]*B);i++) if(a[i]!=c)a[i]=c; else ans++; if(bl[l]!=bl[r]){ reset(bl[r]); for(int i=(bl[r]-1)*B+1;i<=r;i++) if(a[i]!=c)a[i]=c; else ans++; } for(int i=bl[l]+1;i<=bl[r]-1;i++) if(tag[i]!=-1){ if(tag[i]!=c)tag[i]=c; else ans+=B; } else{ for(int j=(i-1)*B+1;j<=i*B;j++) if(a[j]!=c)a[j]=c; else ans++; tag[i]=c; } return ans; } int main(){ memset(tag,-1,sizeof(tag)); int i,j,k; n=gi();B=sqrt(n); for(i=1;i<=n;i++)a[i]=gi(); for(i=1;i<=n;i++)bl[i]=(i-1)/B+1; for(int T=1;T<=n;T++){ int l=gi(),r=gi(),c=gi(); printf("%d\n",Slove(l,r,c)); } return 0; }
數列分塊入門 9
這個我是真的不會,也不想寫。