P7447 [Ynoi2007] rgxsxrs毒瘤数据结构

前言

首A黑题,虽然离不开题解,但是还是很激动

我的想法

首先我是想取维护区间的max,之后假如区间max小于x,那么我们就不用去递归了,之后如果大于就去分别处理,但是可以发现这个做法虽然也有均摊,但是当x很小时候无异于暴力,所以还是要优化

正解

首先是十分有用(** 毒 瘤 ** )的不均匀分块,我们取K(一个基数),之后按照【1,k)【k 2 , k 3) …这样去分块,

之后就可以最多分出来 log ⁡ k a i \log_k ai logkai个块。

我在每一个块上开一个线段树,之后线段数上维护区间最大值,区间和,区间最小值。

不均匀分块目的

我考虑一下按照均匀的值域分块,是不是每一次的修改量十分的大,而不均匀首先块的数量比较少,之后就是每一个数的掉落次数也最多是log次,之后就会有复杂度保证

空间问题

我们发现在线段树上的空间十分着急,怎么办,我们就用时间去换空间,首先线段树上最耗费空间的是不是叶子节点,我们就可以把叶子节点扩大,也就代表我们每一个叶子维护的是原数列的一些数(这里直接暴力处理就可以了,至于满足分块值域其实就在暴力里判断就好了)。

修改操作

首先分三类

小于x的块 ,不管

大于x的块,直接区间打标记修改是不是就好了

x这个块 , 比较麻烦,首先我们去考虑一下这个块也会分成两个部分

上面的我就取直接修改,之后就是看看会不会掉落到其他的块是不是,那么我就在暴力里取加入插入函数是不是就好了

至于x以下的是不是就不用管了

询问

是不是遍历每一个块,把答案综合起来是不是就好了

代码

#include
#define ll long long
#define ls (num << 1)
#define rs (num << 1 | 1)
using namespace std;
const int maxn = 5e5 + 10 , B = 16 , K = 32 ,  df = 2e4 + 7;
void read(int &x){
	int r = 0;
	char s;
	s = getchar();
	while(s < '0' || s > '9') s = getchar();
	while(s >= '0' && s <= '9'){
		r = r * 10 + s - '0';
		s = getchar();
	}
	x = r;
}

int n , m , a[maxn];
int mx[9][df << 1] , mn[9][df << 1];//max min 
ll sum[9][df << 1] , lb[9] , rb[9];//区间和,块的左边界,右边界 
ll ma[9][df << 1] , tag[9][df << 1];//区间有几个数, 懒标记 

int getblk(int now){
	int cnt = 1;
	while(rb[cnt] < now){
		cnt++;
	}
	return cnt;
}//查找值在那一块 

void pushup(int blk , int num){
	mx[blk][num] = max(mx[blk][ls] , mx[blk][rs]);
	mn[blk][num] = min(mn[blk][ls] , mn[blk][rs]);
	ma[blk][num] = ma[blk][ls] + ma[blk][rs];
	sum[blk][num] = sum[blk][ls] + sum[blk][rs];
}

void pushdown(int blk , int num){
	if(!tag[blk][num]) return;
	tag[blk][ls] += tag[blk][num] * (ma[blk][ls] != 0);
	tag[blk][rs] += tag[blk][num] * (ma[blk][rs] != 0);
	if(ma[blk][ls]){
		sum[blk][ls] -= ma[blk][ls] * tag[blk][num];
		mn[blk][ls] -= tag[blk][num];
		mx[blk][ls] -= tag[blk][num];
	} 
	if(ma[blk][rs]){
		sum[blk][rs] -= ma[blk][rs] * tag[blk][num];
		mn[blk][rs] -= tag[blk][num];
		mx[blk][rs] -= tag[blk][num];
	}
	tag[blk][num] = 0;	return;
}

void blk_pushdown(int l,int r,int blk,ll &x){
	if(!x) return;
	for(int i = l; i <= r ; i++) if(a[i] >= lb[blk] && a[i] <= rb[blk]) a[i] -= x;
	x = 0;//清除标记
	return; 
}//底层分块的下传信息,暴力传递  省空间

void blk_pushup(int l , int r , int blk , int num){
	mn[blk][num] = INT_MAX; 
	mx[blk][num] = 0 ; sum[blk][num] = 0 ; ma[blk][num] = 0;
	for(int i = l ; i <= r ; i++){
		if(a[i] >= lb[blk] && a[i] <= rb[blk]){
			sum[blk][num] += a[i] ; 	
			ma[blk][num] ++;
			mx[blk][num] = max(mx[blk][num] , a[i]);
			mn[blk][num] = min(mn[blk][num] , a[i]);
		}
	} return;
} 
//底层分块的上传信息 
void insert(int blk , int num,int l,int r,int c,int d){
	if(r - l + 1 <= K){
		blk_pushdown(l , r , blk , tag[blk][num]);
		a[c] = d;
		blk_pushup(l , r , blk , num);
		//cout<
		return;//假如区间长度小于的话就直接对于序列暴力,之后就可以省空间 
	}
	int mid = (l + r) >> 1;
	pushdown(blk,num);
	if(mid >= c) insert(blk,ls,l,mid,c,d);
	else insert(blk,rs,mid+1,r,c,d);
	pushup(blk,num);
}//插入点

void blk_update(int l,int r,int blk,int x){
	for(int i = l ; i <= r; i++){
		if (a[i] >= lb[blk] && a[i] <= rb[blk] && a[i] > x){
			a[i] -= x;
			if(a[i] < lb[blk]){
				insert(getblk(a[i]),1,1,n,i,a[i]);
			}//插入别的块里面 
		}
	}
}

void blk_que(int blk,int l,int r,int c,int d,ll &ans,int &maxx,int &minn){
	for(int i = max(l,c); i <= min(r,d) ; i++){
		if(a[i] >= lb[blk] && a[i] <= rb[blk]){
			ans += a[i] , maxx = max(maxx,a[i]) , minn = min(minn,a[i]);
		}
	}
	return;
}

void update(int blk,int num,int l,int r,int c,int d,int x){
	if(mx[blk][num]	<= x) return;
	if(r - l + 1 <= K){
		blk_pushdown(l,r,blk,tag[blk][num]);
		c = max(c,l) ; d = min(r,d);
		blk_update(c,d,blk,x);
		blk_pushup(l,r,blk,num);
		return;
	}
	if(c <= l && r <= d && mn[blk][num] - lb[blk] >= x){
		sum[blk][num] -= x * ma[blk][num] , mn[blk][num] -= x , mx[blk][num] -= x;
		tag[blk][num] += x;
		return;
	}//如果完全包含直接打标记 
	int mid = (l + r) >> 1;
	pushdown(blk,num);
	if(mid >= c) update(blk,ls,l,mid,c,d,x);
	if(mid < d) update(blk,rs,mid+1,r,c,d,x);
	pushup(blk,num);
	return;
}
//区间修改

void query(ll &ans, int &maxx, int &minn, int blk, int num, int l, int r,int c,int d){
	if(c <= l && r <= d){
		ans += sum[blk][num] ; maxx = max(maxx,mx[blk][num]) ; minn = min(minn,mn[blk][num]);
		return; 
	}
	if(r - l + 1 <= K){
		blk_pushdown(l,r,blk,tag[blk][num]);
		blk_que(blk,l,r,c,d,ans,maxx,minn);
		blk_pushup(l,r,blk,num);
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(blk,num);
	if(c <= mid) query(ans,maxx,minn,blk,ls,l,mid,c,d);
	if(d > mid) query(ans,maxx,minn,blk,rs,mid+1,r,c,d);
	pushup(blk,num);
	return;
} 

int main(){
	for(int i = 1; i <= 8; i++){
		for(int j = 0; j <= df * 2 - 2; j++){
			mn[i][j] = INT_MAX ; mx[i][j] = 0;
		}
	}//初始化最值
	for(int i = 1; i <= 8 ; i ++) lb[i] = rb[i - 1] + 1 , rb[i] = lb[i] * B - 1; 
	read(n);	read(m);
	for(int i = 1; i <= n; i++){
		read(a[i]);
		int x = a[i];
		insert(getblk(x) , 1 , 1 , n , i , x);
	}
	ll lastans = 0; 
	int maxx = 0 , minn = INT_MAX;
	ll ans = 0;
	 
	while(m --){
		int op;
		read(op);
		if(op == 1){
			int x,y,z;
			read(x) ; x ^= lastans;
			read(y) ; y ^= lastans;
			read(z) ; z ^= lastans;
			for(int i = 1 ; i <= 8 ; i++) if(rb[i] >= x) update(i,1,1,n,x,y,z);
		}
		
		if(op == 2){
			int x,y;
			ans = 0 , maxx = 0,minn = INT_MAX;
			read(x) ; x ^= lastans;
			read(y) ; y ^= lastans;
			for(int i = 1 ;i <= 8 ;i ++){
				query(ans,maxx,minn,i,1,1,n,x,y);
			}
			printf("%lld %d %d\n" , ans, minn , maxx);
			lastans = ans % 1048576 ;
		}
	}
	return 0;
}

你可能感兴趣的:(好题笔记,数据结构)