题意: 有个长度为n的数组,初值全为0. 现有m次操作 每次操作使用题目中的RNG61函数 3次 得到三个数 f1,f2,f3
接着 令 l=min((f1%n)+1,(f2%n)+1); r=max((f1%n)+1,(f2%n)+1); v=f3%(1<<30);
将数组中【l,r】范围内小于v的数全改为v
思路: 基本上是区间更新然后单点查询
线段树预处理O(nlogn),查询O(logn),支持在线修改
ST表预处理O(nlogn),查询O(1),但不支持在线修改
这题不需要在线修改 那么复杂度就是st表更好
平常的st表 是st[i][j]=min/max(st[i][j-1],st[i+2^(j-1)][j-1]); (st[i][j] 表示i~i+2^j -1 范围内的最值)
现在我们知道了 大的区间 所以 要推小的区间 即st[i][j-1]=max(st[i][j-1],st[i][j]) st[i+2^(j-1)][j-1]=max(st[i+2^(j-1)][j-1],st[i][j]);
不断缩小直到区间长度为1 就是单点了
扫描二维码关注公众号,回复:
2714496 查看本文章
代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=100005;
unsigned int x,y,z;
int n,m;
int LOG[N];
unsigned st[N][25];
unsigned int rng()
{
x^=(x<<11);
x^=(x>>4);
x^=(x<<5);
x^=(x>>14);
unsigned int w;
w=x^(y^z);
x=y;
y=z;
z=w;
}
void update(unsigned l,unsigned r,unsigned v)
{
int lg=LOG[r-l+1];
st[l][lg]=max(st[l][lg],v);
st[r-(1<<lg)+1][lg]=max(st[r-(1<<lg)+1][lg],v);
}
int main()
{
LOG[1]=0;
for(int i=2;i<N;i++)
{
LOG[i]=LOG[i>>1]+1;
}
int t;
scanf("%d",&t);
while(t--)
{
memset(st,0,sizeof(st));
scanf("%d%d%u%u%u",&n,&m,&x,&y,&z);
for(int i=1;i<=m;i++)
{
unsigned f[3];
for(int i=0;i<3;i++) f[i]=rng();
unsigned l,r,v;
l=min((f[0]%n)+1,(f[1]%n)+1);
r=max((f[0]%n)+1,(f[1]%n)+1);
v=f[2]%(1<<30);
update(l,r,v);
}
for(int j=20;j>0;j--)
{
for(int i=1;i+(1<<(j-1))-1<=n;i++)
{
st[i][j-1]=max(st[i][j-1],st[i][j]);
st[i+(1<<(j-1))][j-1]=max(st[i+(1<<(j-1))][j-1],st[i][j]);
}
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ans^=((ll)st[i][0]*i);
}
printf("%lld\n",ans);
}
return 0;
}