bzoj3343: 教主的魔法

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3343

思路:分块大法好...

分块,每块内部保持有序。

修改时,给完整的块打标记,两段不完整的暴力改

询问时,给完整的块二分查找,两段不完整的暴力查。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=1000010,maxk=2010;
using namespace std;
int n,m,q;char op[5],ch;

struct Block{
	int a[maxn],b[maxn],add[maxk],pos[maxn],sz;
	void clear(int x){
		int l=(x-1)*sz+1,r=min(x*sz,n);
		for (int i=l;i<=r;i++) b[i]=a[i];
		sort(b+l,b+r+1);
	}
	void change(int x,int y,int v){
		if (pos[x]==pos[y]) for (int i=x;i<=y;i++) a[i]+=v;
		else{
			for (int i=x;i<=pos[x]*sz;i++) a[i]+=v;
			for (int i=(pos[y]-1)*sz+1;i<=y;i++) a[i]+=v;
		}
		clear(pos[x]),clear(pos[y]);
		for (int i=pos[x]+1;i<pos[y];i++) add[i]+=v; 
	}
	int find(int x,int v){
		int l=(x-1)*sz+1,r=min(x*sz,n),mid=(l+r)>>1,cnt=r;
		while (l<=r){
			if (b[mid]>=v) r=mid-1;
			else l=mid+1;
			mid=(l+r)>>1;
		}
		return cnt-l+1;
	}
	int query(int x,int y,int v){
		int sum=0;
		if (pos[x]==pos[y]) for (int i=x;i<=y;i++) sum+=(a[i]+add[pos[i]]>=v);
		else{
			for (int i=x;i<=pos[x]*sz;i++) sum+=(a[i]+add[pos[i]]>=v);
			for (int i=(pos[y]-1)*sz+1;i<=y;i++) sum+=(a[i]+add[pos[i]]>=v);
		}
		for (int i=pos[x]+1;i<pos[y];i++) sum+=find(i,v-add[i]);
		return sum;
	}
}T;

void read(int &x){
	for (ch=getchar();!isdigit(ch);ch=getchar());
	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}

int main(){
	scanf("%d%d",&n,&q);T.sz=(int)sqrt(n);
	for (int i=1;i<=n;i++){
		read(T.a[i]),T.pos[i]=(i-1)/T.sz+1;
	}
	m=n/T.sz+(n%T.sz?1:0);
	for (int i=1;i<=m;i++) T.clear(i);
	for (int i=1,x,y,z;i<=q;i++){
		scanf("%s",op);read(x),read(y),read(z);
		if (op[0]=='M') T.change(x,y,z);
		else printf("%d\n",T.query(x,y,z)); 
	}
	return 0;
}


你可能感兴趣的:(分块)