题目链接
题目大意
T组数据。
每组给一个长度为
n
n
的数组, 初始全为0, 有次操作, 每次操作将区间
[l,r]
[
l
,
r
]
中
a<v
a
<
v
的更新成v。
最后询问
⊕ni=1(i×ai)
⊕
i
=
1
n
(
i
×
a
i
)
输入的数据由题目给的方式生成。
数据范围
1≤T≤100
1
≤
T
≤
100
1≤n≤105
1
≤
n
≤
10
5
1≤m≤5×106
1
≤
m
≤
5
×
10
6
解题思路
比赛的时候这道题是没写出来的, 看到了数据范围肯定是卡线段树, 线段树需要的复杂度是
m∗log(n)
m
∗
l
o
g
(
n
)
再加上T组。 显然TLE, 最后也没有想到正解。
赛后看了一下题解, 豁然开朗, 居然可以这么做, 复杂度一下就降到了
n∗log(n)
n
∗
l
o
g
(
n
)
。
对于每一段区间
[l,r]
[
l
,
r
]
, 可以将其转换为两段二的幂次的长度。
令
d=[log2(r−1+1)]
d
=
[
log
2
(
r
−
1
+
1
)
]
, 则
[l,r]
[
l
,
r
]
可拆成
[l,l+2d−1]
[
l
,
l
+
2
d
−
1
]
和
[r−2d+1,r]
[
r
−
2
d
+
1
,
r
]
, 两个区间, 保证了完全覆盖。 (就是RMQ的思想)。
这样一共最多有
log(n)
l
o
g
(
n
)
种线段,从长度从大到小枚举, 每一个线段可以转换为等分的两部分的线段, 对于完全相同的线段, 维护最大值即可。 这样一直做到长度为1的线段就结束了。
用数组存一下, 那么
Line(i,d)
L
i
n
e
(
i
,
d
)
表示从
i
i
开始这么长的线段的携带的最大值。
每次则可以转移为
最后按照题目要求计算答案即可。
还有就是题目给的生成数据的方式有点坑, 非常慢.
其实可以不用求出
f
f
数组, 每次计算三个值就可以了。 不需要存起来。 同时也可以减少模的次数。
AC代码
/********************************************
*Author* :ZZZZone
*Created Time* : 一 8/ 6 20:26:08 2018
* Ended Time* : 一 8/ 6 20:52:16 2018
*********************************************/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define debug(x) std::cerr << #x << " = " << (x) << std::endl
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;
inline void OPEN(string s){
freopen((s + ".in").c_str(), "r", stdin);
freopen((s + ".out").c_str(), "w", stdout);
}
const int MAXN = 1e5;
const int MAXV = 5e6;
const int INF = 1 << 30;
unsigned X, Y, Z;
int n,m;
int Log2[MAXN+5];
int pow2[30];
LL Line[MAXN+5][35];
unsigned getnum(){
X = X ^ (X << 11);
X = X ^ (X >> 4);
X = X ^ (X << 5);
X = X ^ (X >> 14);
unsigned W = X ^ (Y ^ Z);
X = Y;
Y = Z;
Z = W;
return Z;
}
void Init(){
scanf("%d %d", &n, &m);
scanf("%u %u %u", &X, &Y, &Z);
for(int i = 1; i <= m; i++){
int p = getnum() % n + 1, q = getnum() % n + 1;
int l = min(p, q);
int r = max(p, q);
LL v = getnum() % INF;
int d = Log2[r-l+1];
Line[l][d] = max(Line[l][d], (LL)v);
Line[r-pow2[d] + 1][d] = max(Line[r-pow2[d] + 1][d], (LL)v);
}
}
void Solve(){
for(int d = 30; d >= 1; d--){
for(int i = 1; i +pow2[d] <= n+1; i++){
Line[i][d-1] = max(Line[i][d-1], Line[i][d]);
Line[i+pow2[d-1]][d-1] = max(Line[i+pow2[d-1]][d-1], Line[i][d]);
Line[i][d] = 0LL;
}
}
LL ans = 0LL;
for(int i = 1; i <= n; i++){
ans = ans ^ ((LL)i * Line[i][0]);
Line[i][0] = 0LL;
}
printf("%lld\n", ans);
}
int main()
{
Log2[0] = -1;
pow2[0] = 1;
for(int i = 1; i <= 30; i++) pow2[i] = pow2[i-1] * 2;
for(int i = 1; i <= MAXN; i++) Log2[i] = Log2[i >> 1] + 1;
int T; scanf("%d", &T);
while(T--){
Init();
Solve();
}
return 0;
}