651 字
3 分钟
修改
题目
给定一个长度为 的序列,第 个元素的下标为 ,值为
现有 次操作,每次给定一个区间 ,求该区间的元素和,并将区间内的所有元素值修改为
需要你输出所有操作答案的异或和
由于 很大,我们提供另外一种读入方式
给定一个 ,注意其类型为 unsigned int
,你需要调用 次 函数获得序列,再调用 次获得每次操作的 ,具体代码如下:
const int N = 1e9;const int maxn = 1e7 + 10;unsigned int x = 123456789, y = 362436069, z, a[maxn];int n, q;unsigned int gen(){ unsigned int t; x ^= x << 16; x ^= x >> 5; x ^= x << 1; t = x; x = y; y = z; z = t ^ x ^ y; return z % N + 1;}
int main(){ scanf("%d%d%u", &n, &q, &z); for ( int i = 1; i <= n; ++ i ) a[i] = gen(); for ( int i = 1; i <= q; ++ i ) { int l = gen() % n + 1, r = gen() % n + 1; if ( l > r ) swap(l, r); // do your things ... }}
对于 的数据,满足
对于 的数据,满足
题解
我才不会告诉你考场上我想了1个小时还没想出来
开始想到线段树,看数据范围,妥妥超时
然后就不知道怎么做了
既然查询完接着删除,那么一个数查询完之后接着就可以不用管了
考虑用并查集维护不用管的数,开始的时候开个并查集树
查询的时候,顺便维护一下(把到的fa都指向fa[r + 1]
)
(其实用链表维护也可以过)
我好菜
代码
#include<bits/stdc++.h>#define ll long long
using namespace std;
const int N = 1e9;const int maxn = 1e7 + 10;unsigned int x = 123456789, y = 362436069, z, a[maxn];int n, q;int fa[maxn];unsigned int gen(){ unsigned int t; x ^= x << 16; x ^= x >> 5; x ^= x << 1; t = x; x = y; y = z; z = t ^ x ^ y; return z % N + 1;}
int getfa(int u){ if(fa[u] == u) return u; return fa[u] = getfa(fa[u]);}
void merge(int x, int y){ int xfa = getfa(x), yfa = getfa(y); fa[xfa] = yfa;}
int main(){ scanf("%d%d%u", &n, &q, &z); for (int i = 1;i <= n;i++) a[i] = gen(), fa[i] = i; fa[n + 1] = n + 1; unsigned ll ans = 0; for (int i = 1;i <= q;i++) { int l = gen() % n + 1, r = gen() % n + 1; if (l > r) swap(l, r); unsigned ll b = 0; int g; for(int i = getfa(l);i <= r;i = g) { b += a[i]; g = getfa(i + 1); merge(i, i + 1); } ans ^= b;
} cout << ans;}