刚开题面 式子有点多...不要慌..只是生成操作的数据.(防止因为读入超时.)
题意:长度为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] 则到该区间内暴力更新.也能抖过..