【总结】 暴力数据结构——分块

前言

分块是一种优化暴力的方法,如果想要知道分块有多么神奇,详情参考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

这个我是真的不会,也不想写。
upd: 不过突然发现这道题目也不是很难,码量也不大,就顺手切了

#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<map>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,B,id;
int v[50010],bl[50010];
map<int,int>M;
int val[50010],cnt[50010];
int f[510][510];
vector<int>g[50010];
void pre(int x){
    memset(cnt,0,sizeof(cnt));
    int mx=0,ans=0;
    for(int i=(x-1)*B+1;i<=n;i++){
	cnt[v[i]]++;
	int t=bl[i];
	if(cnt[v[i]]>mx || (cnt[v[i]]==mx && val[v[i]]<val[ans]))ans=v[i],mx=cnt[v[i]];
	f[x][t]=ans;
    }
}
int query(int l,int r,int x){
    int t=upper_bound(g[x].begin(),g[x].end(),r)-lower_bound(g[x].begin(),g[x].end(),l);
    return t;
}
int query(int a,int b){
    int ans,mx;
    ans=f[bl[a]+1][bl[b]-1];
    mx=query(a,b,ans);
    for(int i=a;i<=min(bl[a]*B,b);i++){
	int t=query(a,b,v[i]);
	if(t>mx || (t==mx && val[v[i]]<val[ans]))ans=v[i],mx=t;
    }
    if(bl[a]!=bl[b])
	for(int i=(bl[b]-1)*B+1;i<=b;i++){
	    int t=query(a,b,v[i]);
	    if(t>mx || (t==mx && val[v[i]]<val[ans]))ans=v[i],mx=t;
	}
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);B=sqrt(n);
    for(int i=1;i<=n;i++){
	scanf("%d",&v[i]);
	if(M.find(v[i])==M.end()){
	    M[v[i]]=++id;val[id]=v[i];
	}
	v[i]=M[v[i]];
	g[v[i]].push_back(i);
    }
    for(int i=1;i<=n;i++)bl[i]=(i-1)/B+1;
    for(int i=1;i<=bl[n];i++)pre(i);
    int ans=0;
    while(m--){
	int a,b;scanf("%d%d",&a,&b);
	a=(a+ans-1)%n+1,b=(b+ans-1)%n+1;
	if(a>b)swap(a,b);
	ans=val[query(a,b)];
	printf("%d\n",ans);
    }
    return 0;
}
posted @ 2018-11-06 21:46  cjgjh  阅读(141)  评论(0编辑  收藏  举报