GDCPC2021
A
- 题意
- 给定 n,m
- 求第 k 大的 ∣ x y ∣ , x ∈ [ 0 , n ] , y ∈ [ 0 , m ] |xy|, \quad x \in [0, n], y \in [0, m] ∣xy∣,x∈[0,n],y∈[0,m]
- 题解
- 暴力堆搜索
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 100;
struct Node {
int x, y;
bool operator<(const Node &r) const { return (ll)x * y < (ll)r.x * r.y; }
};
priority_queue<Node> qe;
set<pair<int, int>> s;
void insert(int x, int y) {
if (s.find({x, y}) != s.end()) return;
qe.push({x, y}), s.insert({x, y});
}
int main() {
int n = rad(), m = rad(), k = rad();
insert(n, m);
for (int i = 1; i < k; ++i) {
int x = qe.top().x, y = qe.top().y;
// printf("%d %d\n", x, y);
qe.pop(), s.erase(s.find({x, y}));
insert(x - 1, y), insert(x, y - 1);
}
printf("%lld", qe.top().x * (ll)qe.top().y);
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
B
-
题意
- 记 F F F 是兔子数列的集合
- 将某个数 n 分解成兔子数列的某种表示,即求出 F F F 的某个子集 F ′ F^{\prime} F′,使集合累加值为 n, ∑ f i ∈ F ′ = n \sum_{f_i \in F ^{\prime}} = n ∑fi∈F′=n
- 求 n 的所有表示的累乘值之和,
- 例: 7 = 5 + 2 = 5 + 1 + 1 = 3 + 2 + 1 + 1 7=5+2=5+1+1=3+2+1+1 7=5+2=5+1+1=3+2+1+1
- 得: 5 ∗ 2 + 5 ∗ 1 ∗ 1 + 3 ∗ 2 ∗ 1 ∗ 1 = 21 5*2+5*1*1+3*2*1*1=21 5∗2+5∗1∗1+3∗2∗1∗1=21
-
题解
- 压维线性dp && 数学优化(离谱)
- 首先,我们默认兔子数列是从下标 1 开始的,即 f 1 = 1 , f 2 = 1 , f 3 = 2 f_1 = 1, f_2 = 1, f_3 = 2 f1=1,f2=1,f3=2
- 我们指出兔子数列的一个性质:
∑
i
=
1
n
f
i
<
3
f
n
\sum_{i = 1}^n f_i < 3f_n
∑i=1nfi<3fn
- 证明:
假设: ∑ i = 1 n f i < k f n \sum_{i = 1}^n f_i < k f_n ∑i=1nfi<kfn 成立
∴ ∑ i = 1 n + 1 f i = ∑ i = 1 n f i + f n + 1 < k f n + f n + 1 \therefore \sum_{i = 1}^{n + 1} f_i = \sum_{i = 1}^{n} f_i + f_{n + 1} < k f_n + f_{n + 1} ∴∑i=1n+1fi=∑i=1nfi+fn+1<kfn+fn+1
考察: k f n + f n + 1 < k f n + 1 kf_n + f_{n + 1} < kf_{n + 1} kfn+fn+1<kfn+1
⇔ k f n + ( 1 − k ) f n + 1 < 0 \Leftrightarrow kf_n + (1 - k)f_{n + 1} < 0 ⇔kfn+(1−k)fn+1<0
⇔ k f n + ( 1 − k ) f n + ( 1 − k ) f n − 1 < 0 \Leftrightarrow kf_n + (1 - k)f_{n} + (1-k)f_{n - 1} < 0 ⇔kfn+(1−k)fn+(1−k)fn−1<0
⇔ f n + ( 1 − k ) f n − 1 < 0 \Leftrightarrow f_n + (1 - k)f_{n - 1} < 0 ⇔fn+(1−k)fn−1<0
⇔ f n − 2 + ( 2 − k ) f n − 1 < 0 \Leftrightarrow f_{n - 2} + (2 - k)f_{n - 1} < 0 ⇔fn−2+(2−k)fn−1<0
⇔ f n − 2 < ( k − 2 ) f n − 1 \Leftrightarrow f_{n - 2} < (k - 2)f_{n - 1} ⇔fn−2<(k−2)fn−1
显然,这对 k ≥ 3 k \ge 3 k≥3 成立
∴ ∑ i = 1 n + 1 f i < 3 f n + 1 \therefore \sum_{i = 1}^{n + 1} f_i < 3f_{n + 1} ∴∑i=1n+1fi<3fn+1
那么由数学归纳法: ∑ i = 1 n + 1 f i < 3 f n + 1 , ∀ n ∈ R \sum_{i = 1}^{n + 1} f_i < 3f_{n + 1}, \quad \forall n \in R ∑i=1n+1fi<3fn+1,∀n∈R 成立
- 证明:
- 设 ( i , j ) (i,j) (i,j) 表示前 i 项(包含第 i 项)构造 j 的方案,那么 f i < j < p r e j f_i < j < pre_j fi<j<prej 时,方案才有意义。这意味着前 i 项只能转移到 j ∈ [ f i , 3 f i ) j \in [f_i, 3f_i) j∈[fi,3fi)
- 设 d p ( j ) dp(j) dp(j) 表示题目中的 W ( F ( j ) ) W(F(j)) W(F(j)),那么状态转移方程为: d p j = ∑ f i × d p i dp_j = \sum f_i \times dp_i dpj=∑fi×dpi
- 由于上述 dp 方程合法范围难以把握,所以直接枚举 i 的扩散: d p j + = f i × d p i , j ∈ [ f i , 3 f i ] dp_j += f_i \times dp_i, j \in [f_i, 3f_i] dpj+=fi×dpi,j∈[fi,3fi]
- 值得一提的是,这不就是个压维0-1 背包(队友提醒了我才反应过来)
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 1e7;
const int mod = 998244353;
int f[max_size];
ll pre[max_size];
int fsiz;
inline void init_fi() {
f[++fsiz] = 1, pre[fsiz] = 1;
while (f[fsiz] < maxn) {
++fsiz, f[fsiz] = f[fsiz - 1] + f[fsiz - 2];
pre[fsiz] = pre[fsiz - 1] + f[fsiz];
}
}
int dp[maxn];
int main() {
init_fi();
dp[0] = 1, dp[1] = 1;
for (int i = 2; i <= fsiz; ++i) {
for (int j = min(3 * f[i], maxn - 1); j >= f[i]; --j) {
dp[j] = (dp[j] + (ll)f[i] * dp[j - f[i]]) % mod;
}
}
int Q = rad();
while (Q--) {
int x = rad();
printf("%d\n", dp[x]);
}
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
C
-
题意
- 给定主串 s,数值 k
- 求长度不小于 k 的子串在 s 中出现次数的最大值
-
题解?
- 后缀数组?
D
-
题意
- 给定一个序列 a 1 a_1 a1 ~ a n a_n an
- 操作:选择相邻的两个数,删除较小的那个,并将较大的元素的数值翻倍
- 如果相等,则随机视某个数较大
- 求操作 n - 1 次操作后,哪些元素可能是最后剩下的那个
-
题解
- dfs/双指针 && 剪枝
- 检查每个数,用双指针模拟能否吃掉相邻的数
- 当当前值不小于最大值时,剪枝,返回 1
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 5e5;
int n;
int a[maxn];
int up;
bool visit[maxn];
bool dfs(int num, int l, int r) {
// printf("%d %d\n", l, r);
if (l == 0 && r == n + 1) return 1;
if (num >= up) return 1;
if (l > 0 && num >= a[l])
return dfs(num * 2, l - 1, r);
else if (r <= n && num >= a[r])
return dfs(num * 2, l, r + 1);
return 0;
}
int main() {
n = rad();
up = 0;
rf(i, n) a[i] = rad(), up = max(up, a[i]);
int ans = 0;
rf(i, n) visit[i] = dfs(a[i], i - 1, i + 1), ans += visit[i];
printf("%d\n", ans);
rf(i, n) if (visit[i]) printf("%d ", i);
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
E
- 题意
- 求 [ 0 , 1 0 n ] [0, 10^n] [0,10n] 中,能被 11 整除,或包含数码 k 的数的数量
- n ∈ 1 e 6 , k ∈ 2 e 5 n \in 1e6, k \in 2e5 n∈1e6,k∈2e5
- 题解?
- 数位 dp
F
-
题意
- 给定序列 a 0 a_0 a0 ~ a n a_n an,
- 求 ∑ i = 0 n ∑ j = 0 a i A ( i , j ) \sum_{i = 0}^n\sum_{j = 0}^{a_i}A(i,j) ∑i=0n∑j=0aiA(i,j)
- A ( n , m ) = { 1 , m = 0 0 , m > n n ! ( n − m ) ! , m < = n A(n, m) = \begin{cases} 1, &m = 0\\0, &m > n \\ \frac{n!}{(n-m)!}, &m <= n\end{cases} A(n,m)=⎩⎪⎨⎪⎧1,0,(n−m)!n!,m=0m>nm<=n
-
题解
- 数学 && 线段树
- 设 g ( x , l , r ) = ∑ i = l r A x i g(x, l, r) = \sum_{i = l}^r A_x^i g(x,l,r)=∑i=lrAxi, f ( x , y ) = s u m ( x , 0 , y ) f(x, y) = sum(x, 0, y) f(x,y)=sum(x,0,y)
- 那么问题转化:求 ∑ i = 0 n f ( i , a i ) \sum_{i = 0}^n f(i, a_i) ∑i=0nf(i,ai)
- 现在考察用最少时间获得任意的
f
(
x
,
y
)
f(x,y)
f(x,y)
我们给出两个公式:
f ( x , y ) = 1 + x f ( x − 1 , y − 1 ) f(x, y) = 1 + xf(x - 1, y - 1) f(x,y)=1+xf(x−1,y−1)
f ( x , y ) = f ( x , x ) − ∏ i = x − y x i × f ( x − y − 1 , x − y − 1 ) f(x, y) = f(x, x) - \prod_{i = x - y}^x i \times f(x - y - 1, x - y - 1) f(x,y)=f(x,x)−∏i=x−yxi×f(x−y−1,x−y−1)- 证明公式 2:
∵ A n m = n A n − 1 m − 1 \because A_n^m = n A_{n - 1}^{m - 1} ∵Anm=nAn−1m−1
∴ g ( x , l , r ) = x × g ( x − 1 , l − 1 , r − 1 ) \therefore g(x, l, r) = x \times g(x - 1, l - 1, r - 1) ∴g(x,l,r)=x×g(x−1,l−1,r−1)
由上述递推式得: g ( x , l , r ) = ∏ i = x − l + 1 x i × f ( x − l , r − l ) g(x, l, r) = \prod_{i=x - l + 1}^x i \times f(x - l, r - l) g(x,l,r)=∏i=x−l+1xi×f(x−l,r−l)
又 ∵ f ( x , y ) = f ( x , x ) − g ( x , y + 1 , x ) \because f(x, y) = f(x, x) - g(x, y + 1, x) ∵f(x,y)=f(x,x)−g(x,y+1,x)
∴ f ( x , y ) = f ( x , x ) − ∏ i = x − y x i × f ( x − y − 1 , x − y − 1 ) \therefore f(x, y) = f(x, x) - \prod_{i = x - y}^x i \times f(x - y - 1, x - y - 1) ∴f(x,y)=f(x,x)−∏i=x−yxi×f(x−y−1,x−y−1)
- 证明公式 2:
- 那么,使用公式 1 预处理所有 f ( x , x ) f(x,x) f(x,x),并使用线段树维护累乘值即可
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 1e5;
const int mod = 998241383;
ll fxx[maxn]; // f(x, x)
inline void init() {
fxx[0] = 1;
for (int i = 1; i < maxn; ++i) fxx[i] = (1LL + i * fxx[i - 1]) % mod;
}
ll val[maxn << 2];
inline void maintain(int u) { val[u] = val[u << 1] * val[u << 1 | 1] % mod; }
void build(int u, int l, int r) {
if (l == r) {
val[u] = l;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
maintain(u);
}
ll ask(int u, int l, int r, int s, int t) {
if (s <= l && r <= t) return val[u];
int mid = l + r >> 1;
if (t <= mid) return ask(u << 1, l, mid, s, t);
if (s > mid) return ask(u << 1 | 1, mid + 1, r, s, t);
return ask(u << 1, l, mid, s, t) * ask(u << 1 | 1, mid + 1, r, s, t) % mod;
}
inline int f(int x, int y) {
if (x == y) return fxx[x];
return ((fxx[x] - ask(1, 1, maxn, x - y, x) * fxx[x - y - 1]) % mod + mod) % mod;
}
int main() {
init();
build(1, 1, maxn);
int Q = rad();
while (Q--) {
int n = rad();
ll ans = 0;
for (int i = 0; i <= n; ++i) {
int ai = rad();
ans += f(i, ai), ans %= mod;
}
printf("%d\n", ans);
}
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
G
- 题意
- 给出序列 a 1 a_1 a1 ~ a n a_n an
- 两人轮流操作,Alice 先手
- Alice 可以选择一个奇数并将其分成两个正数,或者删除一个等于1的数。
- Bob 可以选择一个偶数并将其分成两个正数。
- 若某个人无法移动,则输掉比赛。
- 如果两个玩家的移动最佳,求谁是赢家。
- 题解
- 博弈论
- 首先,最优决策应该是操作后使自己还能剩余更多的操作步骤
- 考虑 bob 分解偶数
- 若分解 2,则会给 alice 两步
- 若分解非 2,则会给自己两步
- 考虑 alice 分解奇数
- 分解任何奇数都能给对方一步,给自己一步
- 消除 1,则拖一回合
- 最优决策:
- bob 总是最后分解 2
- alice 总是使偶数部分为 2,给 bob 最烂的牌
- 那么整个过程变成:bob 尽量不分解 2,alice 拖足够多的回合。
- 仔细一想就能发现,两人的最优决策都是把 x 拆成 x - 2 和 2。
- 那么统计序列中大于 2 的偶数能被分解的次数,这是 bob 的操作次数;统计序列中的奇数能被分解的次数,这是 alice 的操作次数。作比较即可。
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 1e5;
int n;
int a[maxn];
int main() {
int Q = rad();
while (Q--) {
n = rad();
ll alice = 0, bob = 0;
rf(i, n) {
int tmp = rad();
if (tmp == 2) continue;
if (tmp & 1)
alice += tmp / 2 + 1;
else
bob += tmp / 2 - 1;
}
if (bob >= alice)
puts("Bob");
else
puts("Alice");
}
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
H
-
题意
- 给定一个无向图,带边权,n 个点,m 条边,没有自环
- 每次移动,其它的边权将变成 f ( x ) = 1 + x 1 − x ( m o d p ) f(x) = \frac{1 + x}{1 - x} \pmod p f(x)=1−x1+x(modp)
- 求从点 1 到点 n 的最短路径
-
题解
- 我们发现边权的变化具有周期 4,那么我们考虑转化边权
- 将图中每个点复制 4 份,
- 那么从点 i 的拷贝 0 到点 j 的拷贝 1,是通过 w i , j w_{i,j} wi,j
- 那么从点 i 的拷贝 1 到点 j 的拷贝 2,是通过 f ( w i , j ) f(w_{i,j}) f(wi,j)
- 这样便转化成求从 1 到 n 的四个拷贝中,最短路的最小值
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 2e5;
int n, m, mod;
void exgcd(ll a, ll b, ll &x, ll &y, ll &g) {
if (b == 0)
x = 1, y = 0, g = a;
else
exgcd(b, a % b, y, x, g), y -= a / b * x;
}
ll inv(ll a, ll b) { // ax = 1 mod b
ll x, y, g;
exgcd(a, b, x, y, g);
return g == 1 ? (x + b) % b : -1;
}
inline ll fx(ll x) { return ((1 + x) * inv((1 - x + mod) % mod, mod)) % mod; }
struct Node {
int to, w;
};
vector<Node> adj[maxn * 4];
inline void add(int l, int r, int w) {
adj[l + 0 * n].push_back({r + 1 * n, w}), w = fx(w);
adj[l + 1 * n].push_back({r + 2 * n, w}), w = fx(w);
adj[l + 2 * n].push_back({r + 3 * n, w}), w = fx(w);
adj[l + 3 * n].push_back({r + 0 * n, w}), w = fx(w);
}
bool visit[maxn * 4];
ll dis[maxn * 4];
struct cmp {
bool operator()(int l, int r) { return dis[l] > dis[r]; }
};
void dijkstra() {
priority_queue<int, vector<int>, cmp> qe;
memset(dis, inf, sizeof(dis));
dis[1] = 0, visit[1] = 1, qe.push(1);
while (!qe.empty()) {
int now = qe.top();
qe.pop();
for (auto p : adj[now]) {
if (visit[p.to]) continue;
visit[p.to] = 1, dis[p.to] = dis[now] + p.w, qe.push(p.to);
}
}
}
int main() {
n = rad(), m = rad(), mod = rad();
rf(i, m) {
int l = rad(), r = rad(), w = rad();
add(l, r, w);
// rf(j, 4) printf("%d ", w), w = fx(w);
// puts("ed");
}
dijkstra();
// rf(i, 4) printf("%d ", dis[i * n]);
// puts("ed");
printf("%lld", min(min(min(dis[n], dis[2 * n]), dis[3 * n]), dis[4 * n]));
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
/*
5 5 11
1 2 3
2 3 4
3 4 5
4 5 6
1 3 7
*/
I
-
题意
- 在三维空间,给出三个平面
- 查询两点间是否被平面分隔
-
题解
- 曲面方程 && 异侧判断
- 对于曲面方程 F ( x , y , z ) = 0 F(x, y, z) = 0 F(x,y,z)=0,若两点在同侧,则 F ( x 1 , y 1 , z 1 ) × F ( x 2 , y 2 , z 2 ) > 0 F(x_1, y_1, z_1)\times F(x_2, y_2, z_2)>0 F(x1,y1,z1)×F(x2,y2,z2)>0
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 100;
// 曲面方程
inline ll fx1(ll x, ll y, ll z) { return 1000 * x - y * y - z * z; }
inline ll fx2(ll x, ll y, ll z) { return -1000 * x - y * y - z * z; }
inline bool aside(int x1, int y1, int z1, int x2, int y2, int z2) {
return (fx1(x1, y1, z1) > 0) ^ (fx1(x2, y2, z2) > 0) || (fx2(x1, y1, z1) > 0) ^ (fx2(x2, y2, z2) > 0);
}
inline bool check(int x1, int y1, int z1, int x2, int y2, int z2) {
return aside(x1, y1, z1, x2, y2, z2) || aside(y1, z1, x1, y2, z2, x2) || aside(z1, x1, y1, z2, x2, y2);
}
inline bool solve(int x1, int y1, int z1, int x2, int y2, int z2) {
return check(x1, y1, z1, x2, y2, z2) || check(y1, x1, z1, y2, x2, z2);
}
int main() {
int Q = rad();
while (Q--) {
int x1 = rad(), y1 = rad(), z1 = rad(), x2 = rad(), y2 = rad(), z2 = rad();
if (solve(x1, y1, z1, x2, y2, z2))
puts("No");
else
puts("Yes");
}
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
J
-
题意
- 每次查询给出一个数 n,
- 求 n 最少能被分解成几个完全平方数的和/差
-
题解
- 枚举 [ 1 , 1 e 5 ] [1,1e5] [1,1e5] 范围内的完全平方数,BFS 即可
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 1e5;
int num[maxn], siz;
int dp[maxn];
void bfs() {
queue<int> qe;
memset(dp, inf, sizeof(dp));
qe.push(0), dp[0] = 0;
while (!qe.empty()) {
int now = qe.front();
qe.pop();
for (int i = 1; i <= siz; ++i) {
int to = now + num[i];
if (to < maxn && dp[to] == inf) dp[to] = dp[now] + 1, qe.push(to);
to = now - num[i];
if (to > 0 && dp[to] == inf) dp[to] = dp[now] + 1, qe.push(to);
}
}
}
int main() {
for (int i = 1; i * i < maxn; ++i) num[++siz] = i * i;
bfs();
int Q = rad();
while (Q--) {
int x = rad();
printf("%d\n", dp[x]);
}
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
K
-
题意
- 强制在线
- 每条线段有一个权值
- 操作 1:添加一条线段,权值为 w
- 操作 2:查询区间内所有完整线段权值的最大差值
-
题解
- 线段树套线段树
- 第一维负责管理线段左端点,第二维负责管理线段右端点
- 查询则查左右端点(即第一二维)都在范围内的即可
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int maxn = 5 + 3e3;
const int max_size = 5 + maxn * 4 + 200000 * 30;
const int maxp = 5 + 3e3;
struct Node {
int up, dwn;
Node(int a = -inf, int b = inf) { up = a, dwn = b; }
};
Node maintain(Node l, Node r) { return {max(l.up, r.up), min(l.dwn, r.dwn)}; }
struct Segtree0 {
int tot;
Node val[max_size];
int ch[max_size][2];
int new_node() { return val[++tot] = Node(), ch[tot][0] = 0, ch[tot][1] = 0, tot; }
int update(int u, int l, int r, int i, int x) {
if (u == 0) u = new_node();
if (l == r) {
val[u] = maintain(val[u], {x, x});
return u;
}
int mid = l + r >> 1;
if (i <= mid)
ch[u][0] = update(ch[u][0], l, mid, i, x);
else
ch[u][1] = update(ch[u][1], mid + 1, r, i, x);
val[u] = maintain(val[ch[u][0]], val[ch[u][1]]);
return u;
}
Node ask(int u, int l, int r, int s, int t) {
if (u == 0) return Node();
if (s <= l && r <= t) return val[u];
int mid = l + r >> 1;
if (t <= mid) return ask(ch[u][0], l, mid, s, t);
if (s > mid) return ask(ch[u][1], mid + 1, r, s, t);
return maintain(ask(ch[u][0], l, mid, s, t), ask(ch[u][1], mid + 1, r, s, t));
}
} seg0;
struct Segtree {
int root[maxp << 2];
void build(int u, int l, int r) {
root[u] = seg0.new_node();
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void update(int u, int l, int r, int p1, int p2, int x) { // (p1, p2) insert(x)
seg0.update(root[u], 1, maxp, p2, x);
if (l == r) return;
int mid = l + r >> 1;
if (p1 <= mid)
update(u << 1, l, mid, p1, p2, x);
else
update(u << 1 | 1, mid + 1, r, p1, p2, x);
}
Node ask(int u, int l, int r, int s1, int t1, int s2, int t2) {
if (s1 <= l && r <= t1) return seg0.ask(root[u], 1, maxp, s2, t2);
int mid = l + r >> 1;
if (t1 <= mid) return ask(u << 1, l, mid, s1, t1, s2, t2);
if (s1 > mid) return ask(u << 1 | 1, mid + 1, r, s1, t1, s2, t2);
return maintain(ask(u << 1, l, mid, s1, t1, s2, t2), ask(u << 1 | 1, mid + 1, r, s1, t1, s2, t2));
}
} seg;
int main() {
seg.build(1, 1, maxp);
int n = rad(), m = rad();
rf(i, n) {
int l = rad(), r = rad(), x = rad();
seg.update(1, 1, maxp, l, r, x);
}
int last = 0;
rf(i, m) {
int op = rad();
if (op == 1) { // update
int p1 = rad() ^ last, p2 = rad() ^ last, x = rad();
seg.update(1, 1, maxp, p1, p2, x);
} else {
int l = rad() ^ last, r = rad() ^ last;
Node tmp = seg.ask(1, 1, maxp, l, r, l, r);
int ans = tmp.up - tmp.dwn;
printf("%d\n", ans);
last = ans;
}
}
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
/*
3 5
2 3 1
5 6 4
4 10 5
2 2 5
2 2 6
2 2 9
1 2 3 20
2 5 14
*/
L
-
题意
- 有三个队相互竞争。
- 首先,以相等的概率选择两个队进行比赛。
- 在每一轮比赛中,败队暂时离开,获胜队继续与未参加本轮比赛的三支球队中的另一支比赛。
- 当一个队总共输掉两轮比赛时,淘汰赛结束。
- 为了简单起见,任何两支球队都有50%的机会战胜对方。
- 求本次淘汰赛的预期轮数。
-
题解
- 概率论
- DFS
#include <algorithm>
#include <cstring>
#include <iostream>
#include <map>
using namespace std;
using ll = long long;
using T = int;
T rad(); // quick read
const int inf = 0x3f3f3f3f;
#define rf(i, n) for (int i = 1; i <= (n); ++i)
const int max_size = 5 + 100;
const int maxn = 5 + 100;
map<int, double> order;
int cnt[maxn];
void dfs(int jump, int left, double p) {
if (cnt[1 << 0] == 2 || cnt[1 << 1] == 2 || cnt[1 << 2] == 2) {
order[cnt[1 << 0] + cnt[1 << 1] + cnt[1 << 2]] += p;
return;
}
if (jump == 0) {
for (int mask = 0x4; mask; mask >>= 1)
dfs(mask, left ^ mask, p / 3);
} else {
for (int mask = 0x4; mask; mask >>= 1) {
if ((left & mask) == 0) continue;
++cnt[mask], dfs(mask, (left ^ mask) | jump, p / 2), --cnt[mask];
}
}
}
int main() {
dfs(0, 0x7, 1);
double ans = 0;
for (auto p : order) ans += p.first * p.second;
cout << ans;
}
T rad() {
T back = 0;
int ch = 0, posi = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
posi = ch ^ '-';
for (; ch >= '0' && ch <= '9'; ch = getchar())
back = (back << 1) + (back << 3) + (ch & 0xf);
return posi ? back : -back;
}
这套题的质量其实很高,主要考察了许多算法与数学的结合,非常考验选手算法池的广度。这次补题,我也是慢吞吞地磨蹭了 2 天才写完这些。比赛上,我可能会直接 g。我感觉到了我做题是真滴慢啊。。。。