//快速幂 intqmi(int a, int b, int p) { int res = 1; while(b) { if(b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; } //求逆元 intinv(int x) { returnqmi(x, MOD - 2, MOD); } int inv2 = inv(2);
#include<bits/stdc++.h> #define int long long usingnamespace std; constint MOD = 998244353;
//快速幂 intqmi(int a, int b, int p) { int res = 1; while(b) { if(b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; } //求逆元 intinv(int x) { returnqmi(x, MOD - 2, MOD); }
voidsolve() { int n, k, w;//n:比赛人数 k:w害怕遇到的人数 w:必胜的人 cin >> n >> k >> w; w -= 1; vector<int> a(k), f(k, 1);//a 害怕遇到的人的编号 f害怕的人到i位置的概率 for(int i = 0; i < k; i++) cin >> a[i], a[i] -= 1;//方便计算父节点 sort(a.begin(), a.end()); int ans = 1, inv2 = inv(2); while(a.size()) { w >>= 1;//往上打一层 vector<int> b, g;//b 临时存储下一轮的节点 g 临时存储下一轮的概率 for(int i = 0; i < a.size();) { //如果和w打 if((a[i] >> 1) == w) ans = ans * (MOD + 1 - f[i] ) % MOD, i += 1; else { b.push_back(a[i] >> 1); if(i + 1 < a.size() && (a[i] >> 1) == (a[i + 1] >> 1)) g.push_back((f[i] + f[i + 1]) * inv2 % MOD), i += 2; elseif((a[i] ^ 1) < n) g.push_back(f[i] * inv2 % MOD), i += 1; else g.push_back(f[i]), i += 1; } } a = b; f = g; n = (n + 1) / 2; } cout << ans << '\n'; } signedmain() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t; cin >> t; while(t--) { solve(); } return0; }
判断一个区间是否是海浪:利用 ST 表,我们可以判断,如果一个区间奇数位的最小值 大于 偶数位的最大值, 那么这个区间是海浪。
需要注意:对于只有一个节点的情况,假设该节点是奇数位,那么因为我们将该点的 max 初始化为 −∞, min 初始化为 +∞ ,所以只有一个点的时候这个点也是海浪
计算最长波浪距离和节点:利用双指针,我们可以得到以 i 为左端点,波浪的最远距离len[i][0],距离最远时的右端点是pos[i]=j
len[i][j] 表示从 i 开始,2j 距离的节点中任一节点,以该节点为左端点的波浪区间的长度最大值
pos[i] 表示以 i 节点为左端点, 最长波浪的右端点;很显然,pos 是的单调递增的
处理询问:对于每一组给定的 [l,r],我们可以将总区间分成三段:在 pos 数组中左端点 i 在 l 左边, 在 l 和 r 中间, 以及在 r 右边。具体如下图,其中黑色线段表示 pos 数组,每个线段的左端点表示 pos 数组中的左端点 i, 右端点表示 pos 数组中的值 j, 即以 i 为左端点的海浪距离最长时的右端点。
#include<bits/stdc++.h> #define int long long usingnamespace std; constint N = 1e5 + 10, mod = 1e9 + 7, INF = (1 << 30) - 1; int n, q, h[N], ql[N], qr[N], lg[N], maxx[N][20], minn[N][20], ans[N], pos[N], len[N][20];
voidInitST() { //初始化log2 lg[1] = 0; for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1; //初始化 奇数位是波谷 偶数位是波峰 for(int i = 1; i <= n; i++) { if(i & 1) maxx[i][0] = h[i], minn[i][0] = INF; else maxx[i][0] = -INF, minn[i][0] = h[i]; } //处理每个区间的(奇数位)最大值和(偶数位)最小值 for(int j = 1; j < 19; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { maxx[i][j] = max(maxx[i][j - 1], maxx[i + (1 << j - 1)][j - 1]); minn[i][j] = min(minn[i][j - 1], minn[i + (1 << j - 1)][j - 1]); } } } //判断是否是海浪 booljudge(int l, int r) { int maxlog = lg[r - l + 1]; int m = min(minn[l][maxlog], minn[r - (1 << maxlog) + 1][maxlog]); int M = max(maxx[l][maxlog], maxx[r - (1 << maxlog) + 1][maxlog]); return m > M; } //处理海浪长度 voidDealLen() { for(int i = n, j = n; i >= 1; i--) { while(i <= j && !judge(i, j)) j--; pos[i] = j; len[i][0] = j - i + 1; } //处理每个区间的最长海浪 for(int j = 1; j < 19; j++) for(int i = 1; i + (1 << j) - 1 <= n; i++) len[i][j] = max(len[i][j - 1], len[i + (1 << j - 1)][j - 1]); } //询问最长海浪 intquerylen(int l, int r) { int maxlog = lg[r - l + 1]; returnmax(len[l][maxlog], len[r - (1 << maxlog) + 1][maxlog]); } //处理询问 voidDealQuery() { for(int i = 1; i <= q; i++) { int l = ql[i], r = qr[i]; int x = lower_bound(pos + l, pos + r + 1, r) - pos; ans[i] = max(ans[i], r - x + 1); if(x - 1 >= l) ans[i] = max(ans[i], querylen(l, x - 1)); } } //反转 voidreverse() { for(int i = 1; i <= n; i++) h[i] = -h[i]; } voidwork() { InitST(); DealLen(); DealQuery(); } voidsolve() { cin >> n >> q; for(int i = 1; i <= n; i++) cin >> h[i]; for(int i = 1; i <= q; i++) cin >> ql[i] >> qr[i]; work(); reverse(); work(); int res = 0; for(int i = 1; i <= q; i++) res = (res + i * ans[i]) % mod;//是q!!!!!是q!!!!!是q!!!!!!! cout << res << '\n'; memset(ans, 0, sizeof ans); } signedmain() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t; cin >> t; while(t--) { solve(); } return0; }