HDU 6356 Glad You Came 区间操作分解,倍增

刚开题面 式子有点多...不要慌..只是生成操作的数据.(防止因为读入超时.)

题意:长度为n的序列a,初始为0,  m次操作(l[i],r[i],v[i]) j=[l[i],r[i]] 若a[j]<v[i] 则令a[j]=v[i].
n<=1e5,m<=5e6. 输出m次操作后,序列a的异或和.


若两个操作的区间都相同,显然其作用的只有v值较大的那一个.
另d=log2(r-l+1).则可以将一个询问拆成两个(l,r,v) -> (l,l+2^d-1),(r-2^d,r).

现在所有操作的区间长度都是2的幂次.
继续将操作区间分解,长度从大到小不断往下拆,例如长度为2^i分解成两个2^i-1区间.直到分解到2^0即可.O(m+nlogn).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ui;
const int N=1e5+5,M=5e6+5;
int T,n,m,lg[N];
ui a[20][N];
ui f[M*3];
ui x,y,z;
ui rng(){
	x=x^(x<<11);
	x=x^(x>>4);
	x=x^(x<<5);
	x=x^(x>>14);
	ui w=x^(y^z);
	x=y;
	y=z;
	z=w;
	return z;
}
int main(){
	scanf("%d",&T);
	lg[2]=1;
	for(int i=3;i<N;i++)	lg[i]=lg[i>>1]+1;
	while(T--){
		memset(a,0,sizeof(a));
		scanf("%d%d%u%u%u",&n,&m,&x,&y,&z);
		for(int i=1;i<=3*m;i++)	
			f[i]=rng();
		int mx=0;
		for(int i=1;i<=m;i++){
			int l=min(f[3*i-2]%n,f[3*i-1]%n)+1;
			int r=max(f[3*i-2]%n,f[3*i-1]%n)+1;
			ui v=f[3*i]%(1<<30);
			int d=lg[r-l+1];
			a[d][l]=max(a[d][l],v);
			a[d][r-(1<<d)+1]=max(a[d][r-(1<<d)+1],v);
			mx=max(d,mx);
		}
		for(int k=mx;k>=1;k--){
			for(int i=1;i<=n;i++){
				if(i+(1<<k)-1>n)	break;
				int d=1<<(k-1);
				a[k-1][i]=max(a[k-1][i],a[k][i]);
				a[k-1][i+d]=max(a[k-1][i+d],a[k][i]);
			}
		}
		ll res=0;
		for(int i=1;i<=n;i++)
			res^=1ll*a[0][i]*i;
		cout<<res<<'\n';
	}
	return 0;
}

线段树维护区间的最小值.若该区间最小值<v[i] 则到该区间内暴力更新.也能抖过..


 

猜你喜欢

转载自blog.csdn.net/noone0/article/details/81481156
今日推荐