2021GDCPC题解(12/12)
这场感觉挺无聊的,都是典题,而且很多题怪怪的
A. An Easy Problem
思路:经典k路归并排序,对于任意一个i属于 [ 1 , n ] [1, n] [1,n], 若固定i,则j从 [ m , 1 ] [m, 1] [m,1]是单调递减的,所以我们可以事先将所有i属于 [ 1 , n ] [1, n] [1,n], j = m j = m j=m的 k k k路起点丢进堆中, 每次弹出最大值,同时更新对应路的指针即可。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
int main() {
ll n, m, k;
cin >> n >> m >> k;
priority_queue<pair<ll, int>> q;
for (int i = 1; i <= n; ++i) {
q.emplace(i * m, m);
}
ll ans = 0;
for (int i = 1; i <= k; ++i) {
auto [x, y] = q.top();
q.pop();
ans = x;
q.emplace(x / y * (y - 1), y - 1);
}
cout << ans;
return 0;
}
B. Byfibonacci
思路:其实就是个物品质量为 f i b fib fib数的01背包,斐波那契前缀和有个经典结论, ∑ i = 1 n f i b ( i ) = f i b ( n + 2 ) − 1 \sum\limits_{i = 1} ^ {n} fib(i) = fib(n + 2) -1 i=1∑nfib(i)=fib(n+2)−1, 故 s u m ( i ) < 3 f i b ( i ) sum(i) < 3fib(i) sum(i)<3fib(i),所以可以 以 2 ∑ i = 1 n f i b ( i ) , ∑ f i b ( i ) < 1 e 7 以2\sum\limits_{i = 1} ^ {n} fib(i), \sum fib(i) < 1e7 以2i=1∑nfib(i),∑fib(i)<1e7的情况通过此题。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
const int maxn = 1e7 + 5;
const int M = 1e7;
const int mod = 998244353;
int dp[maxn], f[maxn];
void read(int&x) {
char c = getchar();
x = 0;
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
}
int main() {
int T;
f[1] = 1;
for (int i = 2; i <= 36; ++i) {
f[i] = f[i - 1] + f[i - 2];
}
dp[0] = dp[1] = 1;
// int cnt = 0;
for (int i = 2; i <= 36; ++i) {
for (int j = min(3 * f[i], M); j >= f[i]; --j) {
dp[j] = (dp[j] + (ll)f[i] * dp[j - f[i]]) % mod;
// cnt++;
}
}
// cout << cnt;
read(T);
for (int i = 1; i <= T; ++i) {
int n;
read(n);
cout << dp[n] << "\n";
}
return 0;
}
C. Conspicuousness
思路: s a m sam sam板子题,在建 s a m sam sam的时候记录串的出现次数,按 p a r e n t s parents parents树上的拓扑序更新再做后缀和即可
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
const int maxn = 6e5 + 5;
struct SAM {
int len[maxn], link[maxn], ch[maxn][26], last, tot, sz[maxn];
int tax[maxn], rk[maxn];
ll sum[maxn];
SAM() {
tot = last = 1;
}
void extend(int c) {
int cur = ++tot, p = last; last = cur;
sz[cur] = 1;
len[cur] = len[p] + 1;
for (; p && !ch[p][c]; p = link[p]) {
ch[p][c] = cur;
}
if (!p)
link[cur] = 1;
else {
int q = ch[p][c];
if (len[p] + 1 == len[q])
link[cur] = q;
else {
int clone = ++tot;
memcpy(ch[clone], ch[q], sizeof(ch[q]));
len[clone] = len[p] + 1;
link[clone] = link[q];
link[q] = link[cur] = clone;
for (; p && ch[p][c] == q; p = link[p])
ch[p][c] = clone;
}
}
}
void solve(int x) {
for (int i = 1; i <= tot; ++i)
tax[len[i]]++;
for (int i = 1; i <= x; ++i)
tax[i] += tax[i - 1];
for (int i = 1; i <= tot; ++i)
rk[tax[len[i]]--] = i;
for (int i = tot; i; --i) {
int now = rk[i];
sz[link[now]] += sz[now];
}
sz[1] = 0;
for (int i = 1; i <= tot; ++i) {
sum[len[i]] = max(sum[len[i]], (ll)sz[i]);
}
for (int i = x - 1; i >= 1; --i) {
sum[i] = max(sum[i + 1], sum[i]);
}
}
} sam;
char s[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, q;
cin >> n >> q;
cin >> (s + 1);
int len = strlen(s + 1);
for (int i = 1; i <= len; ++i) {
sam.extend(s[i] - '1');
}
sam.solve(len);
for (int i = 1; i <= q; ++i) {
int k;
cin >> k;
cout << sam.sum[k] << "\n";
}
return 0;
}
D. Double
思路:暴力判断每个点是否能扩展为1e9即可
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
const int maxn = 5e5 + 5;
int n, a[maxn];
int main() {
IOS
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
vector<int> ans;
for (int i = 1; i <= n; ++i) {
ll sum = a[i];
int l = i - 1, r = i + 1;
while (sum < 1e9) {
if (l >= 1 && a[l] <= sum) {
sum *= 2;
l--;
} else if(r <= n && a[r] <= sum) {
r++;
sum *= 2;
} else {
break;
}
}
if ((!l && r == n + 1) || sum >= 1e9) {
ans.emplace_back(i);
}
}
cout << ans.size() << '\n';
for (auto u : ans) {
cout << u << " ";
}
return 0;
}
E. EXcellent Number
思路:显然有一个裸的数位 d p dp dp, d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示当前枚举到第 i i i位,数字模 11 11 11的值位 j j j,已经匹配了 k k k位的方案数,转移的时候用 k m p kmp kmp处理出 n x t [ i ] [ j ] nxt[i][j] nxt[i][j]表示已经匹配 i i i位下一个数字加 j j j时的最长匹配长度,来优化该转移,但时间复杂度为O ( 1 e 6 ∗ 11 ∗ 1 0 2 ) (1e6 * 11 * 10 ^ 2) (1e6∗11∗102), 时间和空间均无法承受,我们考虑特判 1 0 n 10^n 10n后,对于第一维的递推都是一致的,第一维可以使用矩阵快速幂优化,具体来说就是构造一个 11 l o g k ∗ 11 l o g k 11logk * 11logk 11logk∗11logk的矩阵A,
A [ d p [ 0 ] [ 0 ] d p [ 0 ] [ 1 ] d p [ 0 ] [ 2 ] . . . . ] = [ d p [ 0 ] [ 0 ] d p [ 0 ] [ 1 ] d p [ 0 ] [ 2 ] . . . . ] A\left[\begin{matrix}dp[0][0]\\dp[0][1] \\dp[0][2]\\....\end{matrix} \right] = \left[\begin{matrix}dp[0][0]\\dp[0][1] \\dp[0][2]\\....\end{matrix} \right] A dp[0][0]dp[0][1]dp[0][2].... = dp[0][0]dp[0][1]dp[0][2].... , 我们暴力枚举第二三维以及下一个填的数字通过 n x t nxt nxt在A中构造矩阵即可,不懂的可以看代码。时间复杂度 O ( ( 11 l o g k ) 3 l o g ( n ) ) O((11logk) ^ 3 log(n)) O((11logk)3log(n)), 足以通过本题, 感觉自己真的老了,推个转移矩阵还算了半天。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int mod = 998244353;
int M;
int n, num, dig[13], fail[13], nxt[11][11];
const int maxn = 8e5 + 5;
int dp[maxn][11][7];
void ADD(int &x, int y) {
x += y;
if (x >= mod)
x -= mod;
}
// int dfs(int pos, int mo, int len, bool limit) {
// if (!pos)
// return (!mo || len >= num);
// if (!limit && ~dp[pos][mo][len])
// return dp[pos][mo][len];
// int up = 9;
// int ans = 0;
// for (int i = 0; i <= up; ++i) {
// if (len < num && dig[len + 1] == i) {
// ADD(ans, dfs(pos - 1, (mo * 10 + i) % 11, len + 1, limit && (i == up)));
// } else if (len == num) {
// ADD(ans, dfs(pos - 1, (mo * 10 + i) % 11, len, limit && (i == up)));
// } else {
// ADD(ans, dfs(pos - 1, (mo * 10 + i) % 11, nxt[len][i], limit && i == up));
// }
// }
// if (!limit)
// dp[pos][mo][len] = ans;
// return ans;
// }
struct Mat{
int a[120][120];
void clear(){
memset(a, 0, sizeof a);
}
void init()
{
memset(a, 0, sizeof a);
for(int i = 1; i <= M; i++)
a[i][i] = 1;
}
};
Mat operator *(const Mat &x, const Mat &y){
Mat res;
res.clear();
for(int i = 1; i <= M; i++){
for(int j = 1; j <= M; j++){
for(int k = 1; k <= M; k++){
res.a[i][j] = (res.a[i][j] + (ll)x.a[i][k] * y.a[k][j]) % mod;
}
}
}
return res;
}
Mat mypow(Mat a, int b){
Mat ans;
ans.init();
while(b){
if(b & 1)
ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
}
void KMP() {
fail[1] = 0;
for(int i = 2, j = 0; i <= num; ++i) {
while(j && dig[j + 1] != dig[i])
j = fail[j];
if(dig[j + 1] == dig[i])
++j;
fail[i] = j;
}
for(int i = 0; i <= num; ++i){
for(int j = 0; j <= 9; ++j){
if(i == num)
nxt[i][j] = num;
else{
int k = i;
while(k && dig[k + 1] != j)
k = fail[k];
if(dig[k + 1] == j)
++k;
nxt[i][j] = k;
}
}
}
}
void solve() {
int k;
cin >> n >> k;
num = 0;
while(k) {
dig[++num] = k % 10;
k /= 10;
}
reverse(dig + 1, dig + num + 1);
ll ans = 0;
if(num <= n + 1){
ans = 1;
if(dig[1] != 1)
ans = 0;
for(int i = 2; i <= num; i++)
if(dig[i] != 0)
ans = 0;
}
KMP();
M = (num + 1) * 11;
Mat dp;
dp.clear();
for(int i = 0; i <= num; ++i)
{
for(int j = 0; j <= 10; ++j){
int col = i * 11 + j + 1;
for(int c = 0; c <= 9; c++){
int nxt_mat = nxt[i][c];
int nxt_rem = (j * 10 + c) % 11;
int nxt = nxt_mat * 11 + nxt_rem + 1;
dp.a[nxt][col] = 1;
}
}
}
dp = mypow(dp, n);
for(int i = 0; i <= num; ++i) {
for(int j = 0; j <= 10; ++j) {
int c = i * 11 + j + 1;
if(i == num || j == 0)
ans = (ans + dp.a[c][1]) % mod;
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while(T--) {
// memset(dp, -1, sizeof(dp));
solve();
}
}
F. Fake Math Problem
思路:
设
f
(
i
)
=
∑
j
=
0
i
P
(
i
,
j
)
f(i) = \sum\limits_{j = 0} ^ {i} P(i, j)
f(i)=j=0∑iP(i,j), 不难发现其对答案的贡献为
f
(
i
)
−
f
(
i
−
a
[
i
]
−
1
)
∗
∏
j
=
i
−
a
[
i
]
i
j
f(i) - f(i - a[i] - 1) * \prod\limits_{j = i - a[i]}^{i}j
f(i)−f(i−a[i]−1)∗j=i−a[i]∏ij, 模数不是逆元,使用前缀积的话牵扯到除法,需要crt处理,考虑直接用线段树分治维护连乘即可避免处理逆元问题,时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
题外话:题解居然是O(1.5e8)的做法,真是shit啊,很难让人理解出题人到底是为了什么才出这题的。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define lson p << 1, l, mid
#define rson p << 1 | 1, mid + 1, r
#define ls p << 1
#define rs p << 1 | 1
using namespace std;
using ll = long long;
const int maxn = 1e5 + 5;
const int mod =998241383;
const int M = 1e5;
int a[maxn];
struct SegmentTree {
int sum[maxn << 2];
void pushUp(int p) {
sum[p] = (ll) sum[ls] * sum[rs] % mod;
}
void build(int p, int l, int r) {
if (l == r) {
sum[p] = l;
return;
}
int mid = l + r >> 1;
build(lson);
build(rson);
pushUp(p);
}
int query(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return sum[p];
}
int mid = l + r >> 1;
int ans = 1;
if (L <= mid) {
ans = (ll)ans * query(lson, L, R) % mod;
}
if (R > mid) {
ans = (ll) ans * query(rson, L, R) % mod;
}
return ans % mod;
}
} tr;
int pre[maxn];
int main() {
int T;
tr.build(1, 1, M);
pre[0] = 1;
for (int i = 1; i <= M; ++i) {
pre[i] = ((ll) pre[i - 1] * i + 1) % mod;
}
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 0; i <= n; ++i) {
cin >> a[i];
}
int ans = 0;
for (int i = 0; i <= n; ++i) {
if (i == a[i]) {
ans = (ans + pre[i]) % mod;
} else {
ans = (ans + pre[i]) % mod;
ans = (ans - (ll) tr.query(1, 1, M, i - a[i], i) * pre[i - a[i] - 1] % mod) % mod;
ans = (ans + mod) % mod;
}
}
cout << ans << '\n';
}
return 0;
}
G. Good Game, GG
思路:
果然没有队友我完全不会博弈和构造啊,
v
p
vp
vp的时候放最后写真机智,最后博了
30
m
i
n
30min
30min都过不了
140
+
140+
140+人过的题。
显然当第二个人对2分解的时候,他已经G了,我们考虑每个人的最优策略,对于A,如果是1, 则他的操作比B优1次,对于奇数x,由于2对于B来说是更劣的,所以每次尽量分 x − 2 x - 2 x−2和 2 2 2, 所以A比B多操作 ( x + 1 ) / 2 (x + 1) / 2 (x+1)/2,对于偶数 x , x > 2 x, x>2 x,x>2, 第二个人希望分成任意两个偶数,所以操作次数比A多 x / 2 − 1 x/2 - 1 x/2−1,
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
const int maxn = 1e5 + 5;
int a[maxn];
int main() {
IOS
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
ll odd = 0, even = 0;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
if (a[i] & 1) {
odd += (a[i] + 1) / 2;
} else {
even += a[i] / 2 - 1;
}
}
if (odd > even) {
cout << "Alice\n";
} else {
cout << "Bob\n";
}
}
return 0;
}
H. History
思路:打表发现边长周期为4,分层图最短路即可
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
using PLI = pair<ll, int>;
using P = pair<int, int>;
const ll INF = 9e18;
int mod;
const int maxn = 2e5 + 5;
int mypow(int a, int b) {
int ans = 1;
while (b) {
if (b & 1)
ans = (ll)ans * a % mod;
a = (ll)a * a % mod;
b >>= 1;
}
return ans;
}
vector<P> G[maxn * 4 + 5];
int main() {
IOS
int n, m;
cin >> n >> m >> mod;
priority_queue<PLI, vector<PLI>, greater<PLI>> q;
auto f = [&](int x) {
return (ll)(1 + x) * mypow(((1 - x) % mod + mod) %mod, mod - 2) % mod;
};
for (int i = 1; i <= m; ++i) {
int u, v, w;
cin >> u >> v >> w;
for (int j = 0; j < 4; ++j) {
G[u + j * n].emplace_back(v + (j + 1) % 4 * n, w);
w = f(w);
}
}
vector<ll> d(n * 4 + 5, INF);
d[1] = 0;
q.emplace(d[1], 1);
while (q.size()) {
auto [dd, u] = q.top();
q.pop();
if (dd > d[u])
continue;
for (auto [v, w] : G[u]) {
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
q.emplace(d[v], v);
}
}
}
ll ans = INF;
for (int i = 0; i < 4; ++i) {
ans = min(ans, d[n + i * n]);
}
cout << ans;
return 0;
}
I. Industrial Nuclear Water
思路:异面判断原理为 f ( x 1 , y 1 , z 1 ) ∗ f ( x 2 , y 2 , z 2 ) < 0 f(x1, y1, z1) * f(x2, y2, z2) < 0 f(x1,y1,z1)∗f(x2,y2,z2)<0,分类讨论6种情况即可
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
using ll = long long;
bool check(ll x, ll y, ll z){
return 1000 * z < x * x + y * y;
}
void solve(ll x1, ll y1, ll z1, ll x2, ll y2, ll z2){
bool flag = 1;
if(check(x1, y1, z1) != check(x2, y2, z2))
flag=0;
if(check(x1, y1, -z1) != check(x2, y2, -z2))
flag=0;
if(check(x1, z1, y1)!=check(x2, z2, y2))
flag=0;
if(check(x1, z1, -y1)!=check(x2, z2, -y2))
flag=0;
if(check(z1, y1, x1) != check(z2, y2, x2))
flag=0;
if(check(z1, y1, -x1)!=check(z2, y2, -x2))
flag=0;
cout << (flag ? "Yes\n" : "No\n");
}
int main()
{
int n;
cin >> n;
while(n--) {
ll x1, y1, z1, x2, y2, z2;
cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
bool flag = 1;
if(check(x1, y1, z1) != check(x2, y2, z2))
flag=0;
if(check(x1, y1, -z1) != check(x2, y2, -z2))
flag=0;
if(check(x1, z1, y1)!=check(x2, z2, y2))
flag=0;
if(check(x1, z1, -y1)!=check(x2, z2, -y2))
flag=0;
if(check(z1, y1, x1) != check(z2, y2, x2))
flag=0;
if(check(z1, y1, -x1)!=check(z2, y2, -x2))
flag=0;
cout << (flag ? "Yes\n" : "No\n");
}
return 0;
}
J. Jerry
思路: 最多有 O ( s q r t ( n ) ) O(sqrt(n)) O(sqrt(n))这么多的数, b f s bfs bfs即可,时间复杂度 O ( n s q r t ( n ) ) O(nsqrt(n)) O(nsqrt(n))
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int maxn = 1e5 + 5;
const int M = 1e5;
int a[maxn];
bool vis[maxn];
int main() {
IOS
int t;
cin >> t;
int num = 0;
for (int i = 1; i * i <= M; ++i)
a[++num] = i * i;
queue<int> q;
vector<int> dis(M + 5, 0);
q.push(0);
vis[0] = 1;
while (q.size()) {
auto x = q.front();
q.pop();
for (int i = 1; i <= num; ++i) {
if (x + a[i] <= M && !vis[x + a[i]]) {
vis[x + a[i]] = 1;
dis[x + a[i]] = dis[x] + 1;
q.push(x + a[i]);
}
if (x - a[i] >= 0 && !vis[x - a[i]]) {
vis[x - a[i]] = 1;
dis[x - a[i]] = dis[x] + 1;
q.push(x - a[i]);
}
}
}
while (t--) {
int x;
cin >> x;
cout << dis[x] << "\n";
}
return 0;
}
K. Kera’s line segment
思路:
又是道怪怪题,出题人强制在线想考树套树数据范围又放了二维树状数组过,顺便复习一下树套树,好多年没写过了。
把
(
l
,
r
)
(l, r)
(l,r)转化为二维平面上的点,就是个动态二维数点,我们知道静态二维数点可以使用主席树或离线树状数组/
c
d
q
cdq
cdq统计,但这题是强制在线,考虑树套树或者是分块。
x
x
x轴使用任意序列数据结构即可化去一维,
y
y
y轴需要区间取
m
i
n
min
min操作,最暴力的办法就是权值线段树套权值线段树。
但观察发现,对于
x
x
x轴我们只需要前缀信息,即可以树状数组套权值线段树,再观察发现
y
y
y轴只有后缀询问,可以后缀转前缀,至此,我们只需要支持
x
x
x轴前缀询问,
y
y
y轴前缀最小值即可,坐标只有
3000
3000
3000, 考虑树状数组套树状数组即可解决该问题。
对于树套树,一般是用来处理偏序问题,我们一般可以在$log ^ d(n) 复杂度内进行一次 复杂度内进行一次 复杂度内进行一次d 维偏序的范围查询, 维偏序的范围查询, 维偏序的范围查询,log^d(n)$的复杂度内进行一次单点修改,例如二维情况下,本质是先利用外层降维,内层支持另一维的操作(一般内存也只够套一层)。
简单介绍一些树套树的应用,如果之后有时间会出一篇博客专门讲偏序问题与可持久化与树套树之间的关系
-
树状数组套树
- 优点:常数小,代码短
- 只能维护基于前缀or支持差分的信息
-
线段树套树
- 套平衡树:时间复杂度 O ( l o g 2 n ) O( log^2n ) O(log2n),空间复杂度 O ( n l o g n ) O( nlogn ) O(nlogn)
- 套Trie:$时间复杂度O( lognlogv ) , ), ),空间复杂度O( nlognlogv )$
- 优点:可以维护很多信息,不用支持差分
- 缺点:代码shit, 慢,空间大
-
平衡树套树
- 套平衡树:时间复杂度 O ( l o g 2 n ) ,空间复杂度 O ( n l o g n ) O( log^2n ),空间复杂度O( nlogn ) O(log2n),空间复杂度O(nlogn)
- 套Trie:时间复杂度 O ( l o g n l o g v ) ,空间复杂度 O ( n l o g n l o g v ) O( lognlogv ),空间复杂度O( nlognlogv ) O(lognlogv),空间复杂度O(nlognlogv)
- 优点:可以在线支持第一维插入的问题
- 缺点:代码shit,常数极大,极慢
另外绝大部分问题树套树都可以用分块来解决,分块套树,分块套分块… and so on
虽然一般理论复杂度不够优越,但是常数小加好写啊,还容易debug
像这题就可以用分块套树状数组解决,虽然理论最优为
O
(
n
s
q
r
t
(
n
l
o
g
n
)
)
O(nsqrt(nlogn))
O(nsqrt(nlogn)), 但比线段树套线段树快多了,还好写
不过说实话,感觉树套树现在挺useless的,一辈子遇不到一次,遇到还不如写分块,还是希望不要陷入写大数据结构题误以为自己很强的错觉才是
二维树状数组写法 200ms
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 3e3 + 10;
const int M = 3000;
struct BIT {
#define lowb(i) (i & (-i))
int mx[maxn][maxn], mn[maxn][maxn];
void add(int x, int y, int val) {
x = 3001 - x;
for (int i = x; i <= M; i += lowb(i)) {
for (int j = y; j <= M; j += lowb(j)) {
mx[i][j] = max(mx[i][j], val);
mn[i][j] = min(mn[i][j], val);
}
}
}
int ask(int x, int y) {
x = 3001 - x;
int mxx = 0, mnn = INF;
for (int i = x; i; i-= lowb(i)) {
for (int j = y; j; j -= lowb(j)) {
mnn = min(mnn, mn[i][j]);
mxx = max(mxx, mx[i][j]);
}
}
return mxx - mnn;
}
} bit;
int main() {
IOS
int n, m;
cin >> n >> m;
memset(bit.mn, 0x3f, sizeof(bit.mn));
for (int i = 1; i <= n; ++i) {
int l, r, val;
cin >> l >> r >> val;
bit.add(l, r, val);
}
int last = 0;
for (int i = 1; i <= m; ++i) {
int op, l, r, val;
cin >> op >> l >> r;
if (op == 1) {
cin >> val;
l ^= last;
r ^= last;
bit.add(l, r, val);
} else {
l ^= last;
r ^= last;
int ans = bit.ask(l, r);
cout << ans << '\n';
last = ans;
}
}
return 0;
}
线段树套线段树 900ms
#include<bits/stdc++.h>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define lson lc[p], l, mid
#define rson rc[p], mid + 1, r
using namespace std;
using P = pair<int, int>;
const int INF = 1e9 + 5;
const int maxn = 1e5 + 5;
const int M = 3000;
int rt[maxn << 2], mx[maxn * 200], mn[maxn * 200], lc[maxn * 200], rc[maxn * 200], tot;
void update2(int &p, int l, int r, int x, int val) {
if (!p)
p = ++tot;
mx[p] = max(mx[p], val);
mn[p] = min(mn[p], val);
if (l == r)
return;
int mid = l + r >> 1;
if (x <= mid)
update2(lson, x, val);
else
update2(rson, x, val);
}
void update1(int p, int l, int r, int x, int y, int val) {
update2(rt[p], 0, M, x, val);
if (l == r)
return;
int mid = l + r >> 1;
if (y <= mid)
update1(p << 1, l, mid, x, y, val);
else
update1(p << 1 | 1, mid + 1, r, x, y, val);
}
P get(P a, P b) {
P x = {0, 0};
x.first = min(a.first, b.first);
x.second = max(a.second, b.second);
return x;
}
P query2(int p, int l, int r, int L, int R) {
if (!p)
return {INF, -INF};
if (L <= l && r <= R) {
return {mn[p], mx[p]};
}
int mid = l + r >> 1;
P ans = {INF, -INF};
if (L <= mid)
ans = get(ans, query2(lson, L, R));
if (R > mid)
ans = get(ans, query2(rson, L, R));
return ans;
}
P query(int p, int l, int r, int L1, int R1, int L2, int R2) {
if (L2 <= l && r <= R2) {
return query2(rt[p], 0, M, L1, R1);
}
int mid = l + r >> 1;
P ans = {INF, -INF};
if (L2 <= mid)
ans = get(ans, query(p << 1, l, mid, L1, R1, L2, R2));
if (R2 > mid)
ans = get(ans, query(p << 1 | 1, mid + 1, r, L1, R1, L2, R2));
return ans;
}
int main() {
IOS
fill(mn, mn + maxn * 160 - 1, INF);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
int l, r, x;
cin >> l >> r >> x;
update1(1, 0, M, l, r, x);
}
int last = 0;
for (int i = 1; i <= m; ++i) {
int op, l, r, x;
cin >> op >> l >> r;
if (op == 1) {
cin >> x;
l ^= last;
r ^= last;
update1(1, 0, M, l, r, x);
} else {
l ^= last;
r ^= last;
P ans = query(1, 0, M, l, M, 0, r);
last = ans.second - ans.first;
cout << last << '\n';
}
}
return 0;
}
树状数组套线段树 500ms
#include<bits/stdc++.h>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define lson lc[p], l, mid
#define rson rc[p], mid + 1, r
using namespace std;
using P = pair<int, int>;
const int INF = 1e9 + 5;
const int maxn = 1e5 + 5;
const int M = 3000;
struct BIT {
#define lowb(i) (i & (-i))
int rt[maxn << 2], mx[maxn * 200], mn[maxn * 200], lc[maxn * 200], rc[maxn * 200], tot;
void update(int &p, int l, int r, int x, int val) {
if (!p)
p = ++tot;
mx[p] = max(mx[p], val);
mn[p] = min(mn[p], val);
if (l == r)
return;
int mid = l + r >> 1;
if (x <= mid)
update(lson, x, val);
else
update(rson, x, val);
}
P get(P a, P b) {
P x = {0, 0};
x.first = min(a.first, b.first);
x.second = max(a.second, b.second);
return x;
}
P query2(int p, int l, int r, int L, int R) {
if (!p)
return {INF, -INF};
if (L <= l && r <= R) {
return {mn[p], mx[p]};
}
int mid = l + r >> 1;
P ans = {INF, -INF};
if (L <= mid)
ans = get(ans, query2(lson, L, R));
if (R > mid)
ans = get(ans, query2(rson, L, R));
return ans;
}
void add(int x, int y, int val) {
for (int i = y; i <= M; i += lowb(i)) {
update(rt[i], 0, M, x, val);
}
}
P ask(int x, int y) {
P ans = {INF, -INF};
for (int i = y; i; i -= lowb(i)) {
ans = get(ans, query2(rt[i], 0, M, x, M));
}
return ans;
}
} bit;
int main() {
IOS
fill(bit.mn, bit.mn + maxn * 160 - 1, INF);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
int l, r, x;
cin >> l >> r >> x;
bit.add(l, r, x);
// update1(1, 0, M, l, r, x);
}
int last = 0;
for (int i = 1; i <= m; ++i) {
int op, l, r, x;
cin >> op >> l >> r;
if (op == 1) {
cin >> x;
l ^= last;
r ^= last;
bit.add(l, r, x);
// update1(1, 0, M, l, r, x);
} else {
l ^= last;
r ^= last;
P ans = bit.ask(l, r);
// P ans = query(1, 0, M, l, M, 0, r);
last = ans.second - ans.first;
cout << last << '\n';
}
}
return 0;
}
L. League of Legends
思路:
枚举算出答案为3.5
print(3.5)