20191111 专题:分块

总览:

优雅的暴力……
按照一定的模式乱搞……
分块九题+蒲公英+弹飞绵羊
先放神仙博客:传送门

模式:
预处理:
块的大小的 m m m,维护数据的 v a l val val和记录块数的 b e be be

	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++)   belong[i]=(i-1)/m+1;

维护操作 O ( n ) O(\sqrt n) O(n )
区间修改标记 t a g tag tag
如果左右端点在同一块,暴力维护。(块的大小小于 n \sqrt n n ,时间复杂度为 O ( n ) O(\sqrt n) O(n )
其他,暴力维护左右块,区间维护中间块。(左右块的大小小于 n \sqrt n n ,中间块的个数小于 n \sqrt n n ,时间复杂度为 O ( n ) O(\sqrt n) O(n )

inline void add(int l,int r,int v){
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)   val[i]=v;
		return;
	}
	for(int i=l;belong[i]==belong[l];i++)
		val[i]=v;
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		tag[i]=v;
	for(int i=r;belong[i]==belong[r];i--)
		val[i]=v;
}

查询操作 O ( n ) O(\sqrt n) O(n )
同维护操作。

T1 #6277. 数列分块入门 1

#6277. 数列分块入门 1
题意:区间加,单点查
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=5e4+5;
int n,m;
int val[A];
int tag[A],belong[A];

inline void add(int l,int r,int v){
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)   val[i]+=v;
		return;
	}
	for(int i=l;belong[i]==belong[l];i++)
		val[i]+=v;
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		tag[i]+=v;
	for(int i=r;belong[i]==belong[r];i--)
		val[i]+=v;
}

inline int qurey(int x){
	return val[x]+tag[belong[x]];
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++)   belong[i]=(i-1)/m+1;
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(!opt)    add(l,r,c);
		else    printf("%d\n",qurey(r));
	}
	return 0;
}

T2 #6278. 数列分块入门 2

#6278. 数列分块入门 2
题意:区间加,区间查询个数
思路:保证块内有序,二分查找
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=5e4+5;
int n,m;
int val[A];
int tag[A],belong[A];
vector <int> s[500];

inline void reset(int be){
	s[be].clear();
	for(int i=(be-1)*m+1;i<=min(be*m,n);i++)
		s[be].push_back(val[i]);
	sort(s[be].begin(),s[be].end());
}

inline void add(int l,int r,int v){
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)
			val[i]+=v;
		reset(belong[l]);
		return;
	}
	for(int i=l;belong[i]==belong[l];i++)
		val[i]+=v;
	reset(belong[l]);
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		tag[i]+=v;
	for(int i=r;belong[i]==belong[r];i--)
		val[i]+=v;
	reset(belong[r]);
}

inline int qurey(int l,int r,int v){
	int ans=0;
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)
			if(val[i]+tag[belong[i]]<v) ans++;
		return ans;
	}
	for(int i=l;belong[i]==belong[l];i++)
		if(val[i]+tag[belong[i]]<v) ans++;
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		ans+=lower_bound(s[i].begin(),s[i].end(),v-tag[i])-s[i].begin();
	for(int i=r;belong[i]==belong[r];i--)
		if(val[i]+tag[belong[i]]<v) ans++;
	return ans;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/m+1;
		s[belong[i]].push_back(val[i]);
	}
	for(int i=1;i<=n;i+=m)
		sort(s[belong[i]].begin(),s[belong[i]].end());
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(!opt)    add(l,r,c);
		else    printf("%d\n",qurey(l,r,c*c));
	}
	return 0;
}

T3 #6279. 数列分块入门 3

#6279. 数列分块入门 3
题意:区间加,求前驱
思路:保证块内有序,二分查找
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e5+5;
int n,m;
int val[A];
int belong[A],tag[A];
vector <int> s[500];

inline void reset(int be){
	s[be].clear();
	for(int i=m*(be-1)+1;i<=min(m*be,n);i++){
		s[be].push_back(val[i]+tag[be]);
		val[i]+=tag[be];
	}
	tag[be]=0;
	sort(s[be].begin(),s[be].end());
	return;
}

inline void add(int l,int r,int v){
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)
			val[i]+=v;
		reset(belong[l]);
		return;
	}
	for(int i=l;belong[i]==belong[l];i++)
		val[i]+=v;
	reset(belong[l]);
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		tag[i]+=v;
	for(int i=r;belong[i]==belong[r];i--)
		val[i]+=v;
	reset(belong[r]);
	return;
}

inline int qurey(int l,int r,int v){
	int ans=-1;
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)
			if(val[i]+tag[belong[i]]<v) ans=max(ans,val[i]+tag[belong[i]]);
			return ans;
	}
	for(int i=l;belong[i]==belong[l];i++)
		if(val[i]+tag[belong[i]]<v) ans=max(ans,val[i]+tag[belong[i]]);
	for(int i=belong[l]+1;i<=belong[r]-1;i++){
		if(s[i].front()>=v-tag[i])  continue;
		if(s[i].back()<v-tag[i]){
			ans=max(ans,s[i].back()+tag[i]);
			continue;
		}
		int w=lower_bound(s[i].begin(),s[i].end(),v-tag[i])-s[i].begin()-1;
		ans=max(ans,s[i][w]+tag[i]);
	}
	for(int i=r;belong[i]==belong[r];i--)
		if(val[i]+tag[belong[i]]<v) ans=max(ans,val[i]+tag[belong[i]]);
	return ans;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/m+1;
		s[belong[i]].push_back(val[i]);
	}
	for(int i=1;i<=n;i+=m)
		sort(s[belong[i]].begin(),s[belong[i]].end());
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(opt)	printf("%lld\n",qurey(l,r,c));
		if(!opt)    add(l,r,c);
	}
	return 0;
}

T4 #6280. 数列分块入门 4

#6280. 数列分块入门 4
题意:区间加,区间和
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=5e4+5;
int n,m;
int val[A];
int be[A],tag[A];
int sum[A];

inline void add(int l,int r,int v){
	if(be[l]==be[r]){
		for(int i=l;i<=r;i++){
			val[i]+=v;
			sum[be[i]]+=v;
		}
		return;
	}
	for(int i=l;be[i]==be[l];i++){
		val[i]+=v;
		sum[be[i]]+=v;
	}
	for(int i=be[l]+1;i<=be[r]-1;i++){
		tag[i]+=v;
		sum[i]+=m*v;
	}
	for(int i=r;be[i]==be[r];i--){
		val[i]+=v;
		sum[be[i]]+=v;
	}
	return;
}

inline int qurey(int l,int r,int mod){
	int ans=0;
	if(be[l]==be[r]){
		for(int i=l;i<=r;i++)
			ans=(ans+val[i]+tag[be[i]])%mod;
		return ans;
	}
	for(int i=l;be[i]==be[l];i++)
		ans=(ans+val[i]+tag[be[i]])%mod;
	for(int i=be[l]+1;i<=be[r]-1;i++)
		ans=(ans+sum[i])%mod;
	for(int i=r;be[i]==be[r];i--)
		ans=(ans+val[i]+tag[be[i]])%mod;
	return ans%mod;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++){
		be[i]=(i-1)/m+1;
		sum[be[i]]+=val[i];
	}   
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(!opt)    add(l,r,c);
		if(opt) printf("%lld\n",qurey(l,r,c+1));
	}
	return 0;
}

T5 #6281. 数列分块入门 5

#6281. 数列分块入门 5
题意:区间开方,区间求和
思路:发现 0 , 1 0,1 0,1开方后不变,每一个数至多开方 31 31 31次。将数全为 0 , 1 0,1 0,1的区间打标记即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=5e4+5;
int n,m;
int val[A];
int be[A],sum[A];
bool tag[A];

inline void change(int x){
	if(tag[x])  return;
	int l=(x-1)*m+1,r=x*m;
	bool bz=0;
	for(int i=l;i<=r;i++){
		int v=sqrt(val[i]);
		sum[be[i]]-=val[i]-v;
		val[i]=v;
		if(val[i]!=1&&val[i]!=0)    bz=1;
	}
	if(!bz) tag[be[l]]=1;
}

inline void sq(int l,int r){
	if(be[l]==be[r]){
		if(tag[be[l]])  return;
		for(int i=l;i<=r;i++){
			int v=sqrt(val[i]);
			sum[be[i]]-=val[i]-v;
			val[i]=v;
		}
		return;
	}
	if(!tag[be[l]])
		for(int i=l;be[i]==be[l];i++){
			int v=sqrt(val[i]);
			sum[be[i]]-=val[i]-v;
			val[i]=v;
		}
	for(int i=be[l]+1;i<=be[r]-1;i++)
		change(i);
	if(!tag[be[r]])
		for(int i=r;be[i]==be[r];i--){
			int v=sqrt(val[i]);
			sum[be[i]]-=val[i]-v;
			val[i]=v;
		}
	return;
}

inline int qurey(int l,int r){
	int ans=0;
	if(be[l]==be[r]){
		for(int i=l;i<=r;i++)
			ans+=val[i];
		return ans;
	}
	for(int i=l;be[i]==be[l];i++)
		ans+=val[i];
	for(int i=be[l]+1;i<=be[r]-1;i++)
		ans+=sum[i];
	for(int i=r;be[i]==be[r];i--)
		ans+=val[i];
	return ans;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)
		val[i]=in;
	for(int i=1;i<=n;i++){
		be[i]=(i-1)/m+1;
		sum[be[i]]+=val[i];
	}
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(!opt)    sq(l,r);
		else    printf("%lld\n",qurey(l,r));
	}
	return 0;
}

T6 #6282. 数列分块入门 6

#6282. 数列分块入门 6
题意:单点插入,单点询问
思路:发现某个块过大后整体重建。
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e5+5;
int n,m;
int val[A],be[A];
vector <int> s[500];

inline void rebuild(){
	int id=0,num=0;
	while(!s[id+1].empty()){
		id++;
		for(int y=0;y<s[id].size();y++)
			val[++num]=s[id][y];
		s[id].clear();
	}
	m=sqrt(num);
	for(int i=1;i<=num;i++){
		be[i]=(i-1)/m+1;
		s[be[i]].push_back(val[i]);
	}
	return;
}

inline void putin(int w,int v){
	int id=0,num=0;
	while(num+s[id+1].size()<w){
		id++;
		num+=s[id].size();
	}
	id++;
	s[id].insert(w-num-1+s[id].begin(),v);
	if(s[id].size()>=20*m)  rebuild();
	return;
}

inline int qurey(int w){
	int id=0,num=0;
	while(num+s[id+1].size()<w){
		id++;
		num+=s[id].size();
	}
	id++;
	return s[id][w-num-1];
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)
		val[i]=in;
	for(int i=1;i<=n;i++){
		be[i]=(i-1)/m+1;
		s[be[i]].push_back(val[i]);
	}
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(!opt)    putin(l,r);
		if(opt) printf("%d\n",qurey(r));
	}
	return 0;
}

T7 #6283. 数列分块入门 7

#6283. 数列分块入门 7
题意:区间乘,区间加,单点询问
思路:乘法优先(见线段树2
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e5+5;
const int mod=10007;
int n,m;
int val[A],be[A];
struct Block{
	int add,mul;
}p[A];

inline void pushdown(int id){
	for(int i=(id-1)*m+1;i<=min(n,id*m);i++)
		val[i]=((val[i]*p[id].mul)%mod+p[id].add)%mod;
	p[id].mul=1;
	p[id].add=0;
	return;
}

inline void add(int l,int r,int v){
	if(be[l]==be[r]){
		pushdown(be[l]);
		for(int i=l;i<=r;i++)
			val[i]=(val[i]+v)%mod;
		return;
	}
	pushdown(be[l]);
	for(int i=l;be[i]==be[l];i++)
		val[i]=(val[i]+v)%mod;
	for(int i=be[l]+1;i<=be[r]-1;i++)
		p[i].add=(p[i].add+v)%mod;
	pushdown(be[r]);
	for(int i=r;be[i]==be[r];i--)
		val[i]=(val[i]+v)%mod;
	return;
}

inline void mul(int l,int r,int v){
	if(be[l]==be[r]){
		pushdown(be[l]);
		for(int i=l;i<=r;i++)
			val[i]=(val[i]*v)%mod;
		return;
	}
	pushdown(be[l]);
	for(int i=l;be[i]==be[l];i++)
		val[i]=(val[i]*v)%mod;
	for(int i=be[l]+1;i<=be[r]-1;i++){
		p[i].add=(p[i].add*v)%mod;
		p[i].mul=(p[i].mul*v)%mod;
	}
	pushdown(be[r]);
	for(int i=r;be[i]==be[r];i--)
		val[i]=(val[i]*v)%mod;
	return;
}

inline int qurey(int w){
	return (val[w]*p[(w-1)/m+1].mul%mod+p[(w-1)/m+1].add)%mod;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++)
		be[i]=(i-1)/m+1;
	for(int i=1;i<=n;i+=m)
		p[be[i]].mul=1;
	for(int i=1;i<=n;i++){
		int opt=in,l=in,r=in,c=in;
		if(!opt)    add(l,r,c);
		if(opt==1)  mul(l,r,c);
		if(opt==2)  printf("%lld\n",qurey(r)%mod);
	}
	return 0;
}

T8 #6284. 数列分块入门 8

#6284. 数列分块入门 8
题意:区间查,区间改
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e5+5;
int n,m;
int val[A];
int be[A],tag[A];

inline void reset(int b){
	if(tag[b]==-1)  return;
	for(int i=(b-1)*m+1;i<=b*m;i++)
		val[i]=tag[b];
	tag[b]=-1;
	return;
}

inline int work(int l,int r,int c){
	int ans=0;
	if(be[l]==be[r]){
		reset(be[l]);
		for(int i=l;i<=r;i++)
			if(val[i]==c)   ans++;
			else    val[i]=c;
		return ans;
	}
	reset(be[l]),reset(be[r]);
	for(int i=l;be[i]==be[l];i++)
		if(val[i]==c)   ans++;
		else    val[i]=c;
	for(int i=r;be[i]==be[r];i--)
		if(val[i]==c)   ans++;
		else    val[i]=c;
	for(int i=be[l]+1;i<=be[r]-1;i++){
		if(tag[i]!=-1){
			if(tag[i]!=c)   tag[i]=c;
			else    ans+=m;
		}
		else{
			for(int j=(i-1)*m+1;j<=i*m;j++)
				if(val[j]==c)   ans++;
				else    val[j]=c;
			tag[i]=c;
		}
	}
	return ans;
}

signed main(){
	memset(tag,-1,sizeof(tag));
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)
		val[i]=in;
	for(int i=1;i<=n;i++)
		be[i]=(i-1)/m+1;
	for(int i=1;i<=n;i++){
		int l=in,r=in,c=in;
		printf("%d\n",work(l,r,c));
	}
	return 0;
}

T9 #6285. 数列分块入门 9

#6285. 数列分块入门 9
题意:查询区间众数
思路:见 T 12 蒲 公 英 T12蒲公英 T12
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e5+5;
const int B=600;
int n,m;
int val[A],a[A],maxx;
int be[A];
int s[B][A],p[B][B];
int t[A];
vector <int> w;

inline void lsh(){
	sort(a+1,a+1+n);
	int len=unique(a+1,a+1+n)-(a+1);
	for(int i=1;i<=n;i++){
		val[i]=lower_bound(a+1,a+1+len,val[i])-a;
		maxx=max(maxx,val[i]);
	}
	return;
}

inline void prepare(){
	for(int i=1;i<=n;i++)
		s[be[i]][val[i]]++;
	for(int i=1;i<=be[n];i++)
		for(int j=1;j<=maxx;j++)
			s[i][j]+=s[i-1][j];
	for(int i=1;i<=be[n];i++)
		for(int j=1;j<=be[n];j++){
			int res=1e9,num=0;
			for(int k=(j-1)*m+1;k<=j*m;k++){
				if(s[j][val[k]]-s[i-1][val[k]]>num){
					res=val[k];
					num=s[j][val[k]]-s[i-1][val[k]];
				}
				if(s[j][val[k]]-s[i-1][val[k]]==num&&val[k]<res)
					res=val[k];
			}
			if(i==j){
				p[i][j]=res;
				continue;
			}
			int k=p[i][j-1];
			if(s[j][k]-s[i-1][k]>num){
				res=k;
				num=s[j][k]-s[i-1][k];
			}
			if(s[j][k]-s[i-1][k]==num&&k<res)
				res=k;
			p[i][j]=res;
		}
	return;
}

inline void clean(){
	for(int i=0;i<w.size();i++)
		t[w[i]]=0;
	w.clear();
	return;
}

inline int qurey(int l,int r){
	clean();
	int res=1e9,num=0;
	if(be[l]==be[r]){
		for(int i=l;i<=r;i++){
			t[val[i]]++;
			if(t[val[i]]>num){
				res=val[i];
				num=t[val[i]];
			}
			if(t[val[i]]==num&&val[i]<res)
				res=val[i];
			w.push_back(val[i]);
		}
		return res;
	}
	for(int i=l;be[i]==be[l];i++){
		if(!t[val[i]])  t[val[i]]+=(s[be[r]-1][val[i]]-s[be[l]][val[i]]);
  		t[val[i]]++;
		if(t[val[i]]>num){
			res=val[i];
			num=t[val[i]];
		}
		if(t[val[i]]==num&&val[i]<res)
			res=val[i];
		w.push_back(val[i]);
	}
	for(int i=r;be[i]==be[r];i--){
		if(!t[val[i]])  t[val[i]]+=(s[be[r]-1][val[i]]-s[be[l]][val[i]]);
		t[val[i]]++;
		if(t[val[i]]>num){
			res=val[i];
			num=t[val[i]];
		}
		if(t[val[i]]==num&&val[i]<res)
			res=val[i];
		w.push_back(val[i]);
	}
	if(be[l]==be[r]-1)  return res;
	int k=p[be[l]+1][be[r]-1];
	if(t[k])    return res;
	t[k]+=(s[be[r]-1][k]-s[be[l]][k]);
	if(t[k]>num){
		res=k;
		num=t[k];
	}
	if(t[k]==num&&k<res)
		res=k;
	w.push_back(k);
	return res;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=a[i]=in;
	for(int i=1;i<=n;i++)   be[i]=(i-1)/m+1;
	lsh();
	prepare();
	for(int i=1;i<=n;i++){
		int l=in,r=in;
		printf("%d\n",a[qurey(l,r)]);
	}
	return 0;
}

T10 P4145 上帝造题的七分钟2 / 花神游历各国

P4145 上帝造题的七分钟2 / 花神游历各国
就是 T 5 T5 T5……
(双倍快乐)
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e5+5;
int n,m,q;
int val[A];
int be[A],sum[A];
bool tag[A];

inline void change(int x){
	if(tag[x])  return;
	int l=(x-1)*m+1,r=x*m;
	bool bz=0;
	for(int i=l;i<=r;i++){
		int v=sqrt(val[i]);
		sum[be[i]]-=val[i]-v;
		val[i]=v;
		if(val[i]!=1&&val[i]!=0)    bz=1;
	}
	if(!bz) tag[be[l]]=1;
}

inline void sq(int l,int r){
	if(be[l]==be[r]){
		if(tag[be[l]])  return;
		for(int i=l;i<=r;i++){
			int v=sqrt(val[i]);
			sum[be[i]]-=val[i]-v;
			val[i]=v;
		}
		return;
	}
	if(!tag[be[l]])
		for(int i=l;be[i]==be[l];i++){
			int v=sqrt(val[i]);
			sum[be[i]]-=val[i]-v;
			val[i]=v;
		}
	for(int i=be[l]+1;i<=be[r]-1;i++)
		change(i);
	if(!tag[be[r]])
		for(int i=r;be[i]==be[r];i--){
			int v=sqrt(val[i]);
			sum[be[i]]-=val[i]-v;
			val[i]=v;
		}
	return;
}

inline int qurey(int l,int r){
	int ans=0;
	if(be[l]==be[r]){
		for(int i=l;i<=r;i++)
			ans+=val[i];
		return ans;
	}
	for(int i=l;be[i]==be[l];i++)
		ans+=val[i];
	for(int i=be[l]+1;i<=be[r]-1;i++)
		ans+=sum[i];
	for(int i=r;be[i]==be[r];i--)
		ans+=val[i];
	return ans;
}

signed main(){
	n=in;m=sqrt(n);
	for(int i=1;i<=n;i++)
		val[i]=in;
	for(int i=1;i<=n;i++){
		be[i]=(i-1)/m+1;
		sum[be[i]]+=val[i];
	}
	q=in;
	for(int i=1;i<=q;i++){
		int opt=in,l=in,r=in;
		if(l>r) swap(l,r);
		if(!opt)    sq(l,r);
		else    printf("%lld\n",qurey(l,r));
	}
	return 0;
}

然而还可以用线段树……
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
#define lch p<<1
#define rch p<<1|1

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=1e5+5;
int n,q;
int val[A];
struct Tree{
	int l,r,max,sum;
}tree[4*A];

inline void pushup(int p){
	tree[p].max=max(tree[lch].max,tree[rch].max);
	tree[p].sum=tree[lch].sum+tree[rch].sum;
	return;
}

inline void build(int p,int l,int r){
	tree[p].l=l,tree[p].r=r;
	if(l==r){
		tree[p].max=tree[p].sum=val[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid),build(rch,mid+1,r);
	pushup(p);
	return;
}

inline void sq(int p,int l,int r){
	if(tree[p].l==tree[p].r){
		tree[p].max=sqrt(tree[p].max);
		tree[p].sum=tree[p].max;
		return;
	}
	if(tree[p].max<=1)  return;
	int mid=(tree[p].l+tree[p].r)>>1;
	if(l<=mid)  sq(lch,l,r);
	if(r>=mid+1)    sq(rch,l,r);
	pushup(p);
	return;
}

inline int qurey(int p,int l,int r){
	if(tree[p].l>=l&&tree[p].r<=r)  return tree[p].sum;
	int ans=0;
	int mid=(tree[p].l+tree[p].r)>>1;
	if(l<=mid)  ans+=qurey(lch,l,r);
	if(r>=mid+1)    ans+=qurey(rch,l,r);
	return ans;
}

signed main(){
	n=in;
	for(int i=1;i<=n;i++)   val[i]=in;
	build(1,1,n);
	q=in;
	for(int i=1;i<=q;i++){
		int k=in,l=in,r=in;
		if(l>r) swap(l,r);
		if(!k)  sq(1,l,r);
		else    printf("%lld\n",qurey(1,l,r));
	}
	return 0;
}

T11 P2801 教主的魔法

P2801 教主的魔法
类似 T 2 T2 T2……
(双倍快乐 × 2 \times2 ×2
代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

inline char ch(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline int in{
	int s=0,f=1;char x;
	for(x=ch();!isdigit(x);x=ch())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=ch())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=2e6+5;
int n,q,m;
int val[A];
int tag[A],belong[A];
vector <int> s[1500];
char opt;

inline void reset(int be){
	s[be].clear();
	for(int i=(be-1)*m+1;i<=min(be*m,n);i++)
		s[be].push_back(val[i]+tag[be]);
	tag[be]=0;
	sort(s[be].begin(),s[be].end());
}

inline void add(int l,int r,int v){
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)
			val[i]+=v;
		reset(belong[l]);
		return;
	}
	for(int i=l;belong[i]==belong[l];i++)
		val[i]+=v;
	reset(belong[l]);
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		tag[i]+=v;
	for(int i=r;belong[i]==belong[r];i--)
		val[i]+=v;
	reset(belong[r]);
}

inline int qurey(int l,int r,int v){
	int ans=0;
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++)
			if(val[i]+tag[belong[i]]>=v) ans++;
		return ans;
	}
	for(int i=l;belong[i]==belong[l];i++)
		if(val[i]+tag[belong[i]]>=v) ans++;
	for(int i=belong[l]+1;i<=belong[r]-1;i++)
		ans+=(s[i].end()-lower_bound(s[i].begin(),s[i].end(),v-tag[i]));
	for(int i=r;belong[i]==belong[r];i--)
		if(val[i]+tag[belong[i]]>=v) ans++;
	return ans;
}

signed main(){
	n=in,q=in;m=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/m+1;
		s[belong[i]].push_back(val[i]);
	}
	for(int i=1;i<=n;i+=m)
		sort(s[belong[i]].begin(),s[belong[i]].end());
	for(int i=1;i<=q;i++){
		opt=ch();
		while(opt!='A'&&opt!='M')   opt=ch();
		int l=in,r=in,c=in;
		if(opt=='M')    add(l,r,c);
		else    printf("%d\n",qurey(l,r,c));
	}
	return 0;
}

T12 P4168 [Violet]蒲公英

题目背景
亲爱的哥哥:
你在那个城市里面过得好吗?
我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫 「 绝 望 」 「绝望」 的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受的时候才做出这样的事的……
最近村子里长出了一大片一大片的蒲公英。一刮风,这些蒲公英就能飘到好远的地方了呢。我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢!
哥哥你要快点回来哦!
爱你的妹妹 V i o l e t Violet Violet
A z u r e Azure Azure 读完这封信之后微笑了一下。
“蒲公英吗……”

题目描述
在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。
为了简化起见,我们把所有的蒲公英看成一个长度为n的序列 ( a 1 , a 2 . . a n ) (a_1,a_2..a_n) (a1,a2..an),其中 a i a_i ai为一个正整数,表示第i棵蒲公英的种类编号。
而每次询问一个区间 [ l , r ] [l,r] [l,r],你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。
注意,你的算法必须是在线的!

输入格式
第一行两个整数 n , m n,m n,m,表示有 n n n株蒲公英, m m m 次询问。
接下来一行 n n n个空格分隔的整数 a i a_i ai,表示蒲公英的种类
再接下来 m m m行每行两个整数 l 0 , r 0 l_0,r_0 l0,r0,我们令上次询问的结果为 x x x(如果这是第一次询问,则 x = 0 x=0 x=0)。
l = ( l 0 + x − 1 )   m o d   n + 1 , r = ( r 0 + x − 1 )   m o d   n + 1 l=(l_0+x-1)\bmod n + 1,r=(r_0+x-1) \bmod n + 1 l=(l0+x1)modn+1,r=(r0+x1)modn+1,如果 l > r l>r l>r,则交换 l , r l,r l,r
最终的询问区间为 [ l , r ] [l,r] [l,r]

输出格式
输出 m m m行。每行一个整数,表示每次询问的结果。

输入输出样例
输入
6 3
1 2 3 2 1 2
1 5
3 6
1 5
输出
1
2
1
说明/提示
对于 20% 的数据,保证 1 ≤ n , m ≤ 3000 1\le n,m \le3000 1n,m3000
对于 100% 的数据,保证 1 ≤ n ≤ 40000 , 1 ≤ m ≤ 50000 , 1 ≤ a i ≤ 1 0 9 1\le n \le 40000,1\le m \le 50000,1\le a_i \le 10^9 1n40000,1m50000,1ai109

分块重头戏……
一道黑题……

思路:
因为 n ≤ 40000 , a i ≤ 1 0 9 n\le 40000,a_i\le 10^9 n40000,ai109,所以离散化。

预处理两个数组:
s [ i ] [ j ] s[i][j] s[i][j]为第 i i i块中 j j j出现的次数。
p [ i ] [ j ] p[i][j] p[i][j]为第 i i i块到第 j j j块的众数。

发现区间 [ l , r ] [l,r] [l,r]的众数属于中间整块的众数和散块中的数的并集。那么统计这些数的个数 ( ≤ 2 × n + 1 ) (\le 2\times \sqrt n +1) (2×n +1)即可。

统计数的个数(用桶统计):
散块暴力扫描,整块类似前缀和, O ( 1 ) O(1) O(1)查询。

预处理:
s [ i ] [ j ] s[i][j] s[i][j](时间复杂度 O ( n ) O(n) O(n)):扫一遍,求前缀和。
p [ i ] [ j ] p[i][j] p[i][j](时间复杂度 O ( n n ) O(n\sqrt n) O(nn )):
p [ i ] [ j ] p[i][j] p[i][j]的范围为 p [ i ] [ j − 1 ] p[i][j-1] p[i][j1]与第 j j j块中出现的数的并集。
两成循环枚举 i , j i,j i,j,一层循环枚举第 j j j块中出现的数,通过 s s s数组求出现次数。

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

inline char ch(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}

inline int in{
	int s=0,f=1;char x;
	for(x=ch();!isdigit(x);x=ch())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=ch())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=5e4+5;
const int B=1e3+5;
int n,m,blo;
int val[A],a[A],maxx;
int be[A];
int p[B][B];
int s[B][A];
int before,l,r;
int t[A];
vector <int> clean;

inline void lsh(){
	sort(a+1,a+1+n);
	int len=unique(a+1,a+1+n)-(a+1);
	for(int i=1;i<=n;i++){
		val[i]=lower_bound(a+1,a+1+len,val[i])-a;
		maxx=max(maxx,val[i]);
	}
	return;
}

inline void prepare(){
	for(int i=1;i<=n;i++)
		s[be[i]][val[i]]++;
	for(int i=1;i<=be[n];i++)
		for(int j=1;j<=maxx;j++)
			s[i][j]+=s[i-1][j];
	for(int i=1;i<=be[n];i++)
		for(int j=i;j<=be[n];j++){
			int res=1e9,num=0;
			for(int k=(j-1)*blo+1;k<=j*blo;k++){
				if(s[j][val[k]]-s[i-1][val[k]]>num){
					res=val[k];
					num=s[j][val[k]]-s[i-1][val[k]];
				}
				if(s[j][val[k]]-s[i-1][val[k]]==num&&val[k]<res)
					res=val[k];
			}
			if(i!=j){
				int k=p[i][j-1];
				if(s[j][k]-s[i-1][k]>num){
					res=k;
					num=s[j][k]-s[i-1][k];
				}
				if(s[j][k]-s[i-1][k]==num&&k<res)
					res=k;
			}
			p[i][j]=res;
		}
	return;
}

inline int qurey(int l,int r){
	if(!clean.empty()){
		for(int i=0;i<clean.size();i++)
			t[clean[i]]=0;
		clean.clear();
	}
	int res=1e9,num=0;
	if(be[l]==be[r]){
		for(int i=l;i<=r;i++){
			t[val[i]]++;
			if(t[val[i]]>num){
				res=val[i];
				num=t[val[i]];
			}
			if(t[val[i]]==num&&val[i]<res)
				res=val[i];
			clean.push_back(val[i]);
		}
		return res;
	}
	for(int i=l;be[i]==be[l];i++){
		if(!t[val[i]])  t[val[i]]+=(s[be[r]-1][val[i]]-s[be[l]][val[i]]);
		t[val[i]]++;
		if(t[val[i]]>num){
			res=val[i];
			num=t[val[i]];
		}
		if(t[val[i]]==num&&val[i]<res)
			res=val[i];
		clean.push_back(val[i]);
	}
	for(int i=r;be[i]==be[r];i--){
		if(!t[val[i]])  t[val[i]]+=(s[be[r]-1][val[i]]-s[be[l]][val[i]]);
		t[val[i]]++;
		if(t[val[i]]>num){
			res=val[i];
			num=t[val[i]];
		}
		if(t[val[i]]==num&&val[i]<res)
			res=val[i];
		clean.push_back(val[i]);
	}
	if(be[l]==be[r]-1)  return res;
	int k=p[be[l]+1][be[r]-1];
	if(t[k])    return res;
	t[k]+=(s[be[r]-1][k]-s[be[l]][k]);
	if(t[k]>num){
		res=k;
		num=t[k];
	}
	if(t[k]==num&&k<res)
		res=k;
	clean.push_back(k);
	return res;
}

signed main(){
	n=in,m=in;blo=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=a[i]=in;
	for(int i=1;i<=n;i++)   be[i]=(i-1)/blo+1;
	lsh();
	prepare();
	for(int i=1;i<=m;i++){
		l=in,r=in;
		l=(l+before-1)%n+1,r=(r+before-1)%n+1;
		if(l>r) swap(l,r);
		printf("%d\n",before=a[qurey(l,r)]);
	}
	return 0;
}

T13 P3203 [HNOI2010]弹飞绵羊

P3203 [HNOI2010]弹飞绵羊
题目描述
某天, L o s t m o n k e y Lostmonkey Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始, L o s t m o n k e y Lostmonkey Lostmonkey在地上沿着一条直线摆上 n n n个装置,每个装置设定初始弹力系数 k i k_i ki,当绵羊达到第 i i i个装置时,它会往后弹 k i k_i ki步,达到第 i + k i i+k_i i+ki个装置,若不存在第 i + k i i+k_i i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣, L o s t m o n k e y Lostmonkey Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

输入格式
第一行包含一个整数 n n n,表示地上有 n n n个装置,装置的编号从 0 到 n − 1 0到n-1 0n1
接下来一行有 n n n个正整数,依次为那 n n n个装置的初始弹力系数。
第三行有一个正整数 m m m,接下来 m m m行每行至少有两个数 i , j i,j i,j,若 i = 1 i=1 i=1,你要输出从 j j j出发被弹几次后被弹飞,若 i = 2 i=2 i=2则还会再输入一个正整数 k k k,表示第 j j j个弹力装置的系数被修改成 k k k

输出格式
对于每个 i = 1 i=1 i=1的情况,你都要输出一个需要的步数,占一行。

输入输出样例
输入
4
1 2 1 1
3
1 1
2 1 1
1 1
输出
2
3
说明/提示
对于20%的数据 n , m < = 10000 n,m<=10000 n,m<=10000,对于100%的数据 n < = 200000 , m < = 100000 n<=200000,m<=100000 n<=200000,m<=100000

拿到题一脸懵逼

思路:
对于每一个块中的每一个点,维护它到下一个块中所需的次数,所到达的点,这样每一个询问只需要跳 n \sqrt n n 次。
(倒着处理贼方便)

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

inline char ch(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline int in{
	int s=0,f=1;char x;
	for(x=ch();!isdigit(x);x=ch())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=ch())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

//编号1-n
const int A=4e5+5;
int n,m,blo;
int val[A];
int be[A];
int s[A],to[A];

inline void prepare(){
	for(int i=n;i>0;i--){
		if(be[i]==be[i+val[i]])
			to[i]=to[i+val[i]],s[i]=s[i+val[i]]+1;
		else
			to[i]=i+val[i],s[i]=1;
	}
	return;
}

inline void change(int w,int v){
	val[w]=v;
	for(int i=be[w]*blo;i>(be[w]-1)*blo;i--){
		if(be[i]==be[i+val[i]])
			to[i]=to[i+val[i]],s[i]=s[i+val[i]]+1;
		else
			to[i]=i+val[i],s[i]=1;
	}
	return;
}

inline int qurey(int w){
	int ans=0,now=w;
	while(be[now]){
		ans+=s[now];
		now=to[now];
	}
	return ans;
}

signed main(){
	n=in;blo=sqrt(n);
	for(int i=1;i<=n;i++)   val[i]=in;
	for(int i=1;i<=n;i++)   be[i]=(i-1)/blo+1;
	prepare();
	m=in;
	for(int i=1;i<=m;i++){
		int opt=in,u=in,v;
		if(opt==1)
			printf("%d\n",qurey(++u));
		if(opt==2){
			v=in;u++;
			change(u,v);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值