2026.7.7 考试总结
赤石总结
估:100 + 100 + 100 + 30
得:100 + 40 + 100 + 45 rk2
前提紧要
你说的对,但这就是牛客。这 t4 真的是黑吧,调了一下午一晚上都弄不出来,还是 Gemini 强啊。妈的沙比出题人诗人啊,T2 神人贪心优化搜索,T4 沙比斜率优化,算了不多喷了。
T1
直接算期望即可。签不多说。
code
#include <bits/stdc++.h>
using namespace std;
#define int int
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 1e5 + 18;
int n, a[maxn], b[maxn];
double ans[maxn], tot = 0;
vector <int> vec[maxn * 2];
void ACehomoxue() {
n = rd();
for(int i = 1; i <= n; i++) {
a[i] = rd(), b[i] = rd();
vec[0].push_back(i);
vec[a[i]].push_back(i);
vec[b[i]].push_back(i);
vec[a[i] + b[i]].push_back(i);
}
for(int i = 2 * 1e5; i + 1; i--) {
if(!vec[i].size()) continue;
for(int x : vec[i]) {
double cnt = tot;
for(int u : {0, 1}) for(int v : {0, 1}) if(a[x] * u + b[x] * v > i) cnt -= 0.25;
ans[x] += 0.25 * cnt;
}
tot += 0.25 * vec[i].size();
}
for(int i = 1; i <= n; i++) printf("%.10lf\n", ans[i] + 1);
}
T2
考虑贪心地反着搜。
当 \(x = 0\),那么 \(x \gets 1\)。
当 \(x < 0\),若 \(x \ge -1000\) 就进行反向一操作;否则,若 \(x \equiv 0 \pmod 2\) 那么优先 \(x \gets \frac x 2\),否则再 \(x = x \times 3 + 1\)。
当 \(x > 0\),显然尽量要让 \(x\) 一直减小,那么如果 \(x \equiv 0 \pmod 2\) 优先 \(x \gets \frac x 2\),否则就 \(x \gets 3x + 1\)。
你说的对但是这样就过了。
code
#include <bits/stdc++.h>
using namespace std;
#define int int
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
// freopen("b.in", "r", stdin);
// freopen("b.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 6 * 1e7 + 6;
int q, d, l;
vector <int> ans;
void sol(int x) {
ans.clear();
while(x ^ 1) {
if(x == 0) x = 1;
else if(x < 0) {
if(x + d > 0 && min(abs(x), abs(x + d)) <= l) x += d;
else if(x < -1000) {
if(x % 2 == 0) x /= 2;
else x = 3 * x + 1;
} else x += d;
} else {
if(x % 2 == 0) x /= 2;
else x = 3 * x + 1;
}
ans.push_back(x);
}
}
void ACehomoxue() {
q = rd(), d = rd(), l = rd();
for(int n; q--; ) {
n = rd();
if(n == 1) { cout << "0 1\n"; continue; }
sol(n);
cout << ans.size() << ' ';
reverse(ans.begin(), ans.end());
ans.push_back(n);
for(int x : ans) cout << x << ' ';
el;
}
}
T3
其实和昨天的 T4 很像吧,对于 \((u, v)\) 的贡献,设 \(dep_u = a\),\(dep_v = c\),\(dep_{lca(u, v)} = b\),那么把 \(dis(u, v)\) 拆开得到:
然后这个就一个一个可以维护了。树剖,每个点都维护一下自己子树内、不包括重儿子的子树内的信息,开 \(9\) 个树状数组维护即可,由于是树状数组所以常数其实很小。
code
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
// freopen("c.in", "r", stdin);
// freopen("c.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 2 * 1e5 + 18;
int n, k, ans[maxn];
vector <int> vec[maxn];
class BIT {
int c[maxn];
public:
void modify(int i, int x) { for(; i < maxn; i += lowbit(i)) c[i] += x; }
il int sum(int i) { int res = 0; for(; i; i -= lowbit(i)) res += c[i]; return res; }
il int query(int l, int r) { return sum(r) - (l > 1 ? sum(l - 1) : 0); }
} kk, kb2, bc, c, kb, c2, subc2, subc, subk;
int in[maxn], out[maxn], dfn, dep[maxn], siz[maxn], tot, head[maxn], be[maxn], pa[maxn], son[maxn];
void predfs(int x, int fa) {
pa[x] = fa;
siz[x] = 1;
dep[x] = dep[fa] + 1;
for(int to : vec[x]) {
if(to == fa) continue;
predfs(to, x);
siz[x] += siz[to];
if(siz[to] > siz[son[x]]) son[x] = to;
}
}
void dfs(int x, int fa) {
in[x] = ++dfn;
if(x == son[fa]) be[x] = be[fa];
else be[x] = ++tot, head[tot] = x;
if(son[x]) dfs(son[x], x);
for(int to : vec[x]) if(to != fa && to != son[x]) dfs(to, x);
out[x] = dfn;
}
void add(int x) {
subk.modify(in[x], 1);
subc.modify(in[x], dep[x]);
subc2.modify(in[x], dep[x] * dep[x]);
for(int p = x; p; p = pa[head[be[p]]]) {
const int &pt = in[p];
kk.modify(pt, 1);
kb2.modify(pt, dep[p] * dep[p]);
bc.modify(pt, dep[p] * dep[x]);
c.modify(pt, dep[x]);
kb.modify(pt, dep[p]);
c2.modify(pt, dep[x] * dep[x]);
}
}
void del(int x) {
subk.modify(in[x], -1);
subc.modify(in[x], -dep[x]);
subc2.modify(in[x], -dep[x] * dep[x]);
for(int p = x; p; p = pa[head[be[p]]]) {
const int &pt = in[p];
kk.modify(pt, -1);
kb2.modify(pt, -dep[p] * dep[p]);
bc.modify(pt, -dep[p] * dep[x]);
c.modify(pt, -dep[x]);
kb.modify(pt, -dep[p]);
c2.modify(pt, -dep[x] * dep[x]);
}
}
#define qry query(l, r)
#define gt(x) (x.query(in[fa], out[fa]) - x.query(in[h], out[h]))
il int get(int x) {
int ans = (dep[x] - 1) * (dep[x] - 1) + (subk.query(in[x], out[x]) * dep[x] * dep[x] - 2 * dep[x] * subc.query(in[x], out[x]) + subc2.query(in[x], out[x]));
for(int p = x, h, l, r, fa; p; p = pa[head[be[p]]]) {
h = head[be[p]], l = in[h], r = in[p] - 1, fa = pa[h];
ans += kk.qry * dep[x] * dep[x] + 4 * kb2.qry + 2 * dep[x] * c.qry - 4 * dep[x] * kb.qry - 4 * bc.qry + c2.qry;
if(fa) {
ans += gt(subk) * (dep[x] * dep[x] + 4 * dep[fa] * dep[fa] - 4 * dep[x] * dep[fa]) + gt(subc) * (2 * dep[x] - dep[fa] * 4) + gt(subc2);
}
}
return ans;
}
void ACehomoxue() {
n = rd() + 1, k = rd();
for(int i = 1, u, v; i < n; i++) {
u = rd() + 1, v = rd() + 1;
vec[u].push_back(v);
vec[v].push_back(u);
}
predfs(1, 0);
dfs(1, 0);
for(int i = 2; i <= n; i++) {
if(i - k - 1 >= 2) del(i - k - 1);
cout << get(i); el;
add(i);
}
}
T4
这里不讲我考场的 \(O(T ^ 2)\) 做法了因为正确性待定。
我们首先考虑,设 \(calc(t)\) 表示两次到门口的时间只差为 \(t\),求其最大的贡献,那么有两种,一是要去沙发,二是一直在门口带着,那么有:
首先可以有 \(dp_{i}\) 表示 \(i\) 时刻回到门口的最大值,那么枚举上一次到门口的时刻并算每个选手的贡献有 \(O(n T ^ 2)\) 复杂的算法,其中 \(T = \max_i \{t_i + p_i\}\),前缀和优化可以做到 \(O(T ^ 2)\)。
我们把 \(dp_{i}\) 打表输出来,我们发现对于每个 \([t_i, t_i + p_i]\) 都成先单减再单增的趋势,这意味着最大值只可能在 \(t_i\) 或 \(t_i + p_i\) 取到,中间都是没有用的,那么我们只需要枚举每一个 \(t = 0 / t_i / t_i + p_i\) 就行了,这样子就只有 \(n\) 个时间点,直接转移是 \(O(n ^ 2)\) 的,还有判断合法性和每个选手的贡献的细节可以看下面的代码。
70pts code
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
// freopen("7.in", "r", stdin);
// freopen("d.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 2 * 1e5 + 18;
int n, x00, x01, x10, x11, l, f[maxn], t[maxn], p[maxn], m, ans = -1e18;
int a[maxn], dp[maxn];
vector <pii> vec;
il int calc(int x) { return max(-l * (x01 + x10) + (x - 2 * l) * x11, -x * x00); }
void ACehomoxue() {
n = rd(), l = rd();
x00 = rd(), x01 = rd(), x10 = rd(), x11 = rd();
m = 0;
for(int i = 1; i <= n; i++) {
t[i] = rd(), p[i] = rd(), f[i] = rd();
a[++m] = t[i], a[++m] = t[i] + p[i];
vec.push_back({t[i], i});
}
sort(a + 1, a + m + 1);
sort(vec.begin(), vec.end());
m = unique(a + 1, a + m + 1) - a - 1;
for(int i = 1, sum, val; i <= m; i++) {
dp[i] = -1e18;
auto p2 = lower_bound(vec.begin(), vec.end(), make_pair(a[i], n + 1));
auto p1 = p2;
bool flag = false;
sum = val = 0;
for(int j = i - 1; j + 1; j--) {
auto pt = lower_bound(vec.begin(), vec.end(), make_pair(a[j], n + 1));
while(p1 != pt) {
p1--;
int x = p1 -> second;
if(t[x] + p[x] < a[i]) { flag = true; break; }
sum += f[x] * (t[x] + p[x] - a[i]);
}
if(flag) break;
val = calc(a[i] - a[j]) + dp[j] + sum;
dp[i] = max(dp[i], val);
if(p2 == vec.end() && p1 != p2) ans = max(ans, val);
}
}
cout << ans;
el;
}
/*
g++ -std=c++17 -O2 -o output\d.exe d.cpp && cd output && start tester.exe && cd..
*/
我们发现,如果我们要优化,那么瓶颈就在 \(calc(x)\) 的求值是困难的。我们把其两种拆开转移取最大值,我们发现转移都能成 \(dp_i = \max \{k a_i + b\}\) 的形式,斜率优化即可。
code
由于这里很多细节我也不太明白,所以代码调了很久也没有调出来。人家 Gemini 强,不仅一遍过而且注释也写得巴巴适适的。
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
int t = 1;
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 2 * 1e5 + 18;
int n, x00, x01, x10, x11, l, m, ans = -1e18;
int t[maxn], p[maxn], f[maxn], a[maxn], dp[maxn], nxt[maxn];
// 前缀和数组,用于计算区间内选手的贡献
int sum_f[maxn], sum_ftp[maxn], sum_fp[maxn];
// k1, b1 维护 Case 1 的斜率和截距
int k1[maxn], b1[maxn];
// s1 维护正常 DP 的单调队列,ans_s1 维护专门用于计算最终完成工作时刻的队列
deque <int> s1, ans_s1;
void ACehomoxue() {
n = rd(), l = rd();
x00 = rd(), x01 = rd(), x10 = rd(), x11 = rd();
m = 0;
a[++m] = 0; // 时刻 0 作为一个关键基准点
int max_t = 0; // 记录最后一名选手的到达时间
for(int i = 1; i <= n; i++) {
t[i] = rd(), p[i] = rd(), f[i] = rd();
a[++m] = t[i];
a[++m] = t[i] + p[i];
max_t = max(max_t, t[i]);
}
// 离散化关键时刻
sort(a + 1, a + m + 1);
m = unique(a + 1, a + m + 1) - a - 1;
// 建立选手属性的前缀和
for(int i = 1; i <= n; i++) {
int x = lower_bound(a + 1, a + m + 1, t[i]) - a;
sum_f[x] += f[i];
sum_ftp[x] += f[i] * (t[i] + p[i]);
sum_fp[x] += f[i] * p[i];
}
for(int i = 1; i <= m; i++) {
sum_f[i] += sum_f[i - 1];
sum_ftp[i] += sum_ftp[i - 1];
sum_fp[i] += sum_fp[i - 1];
}
// 初始化 nxt 数组为无穷大(时间概念)
for(int i = 0; i <= m + 2; i++) nxt[i] = 2e18;
for(int i = 1; i <= n; i++) {
int x = lower_bound(a + 1, a + m + 1, t[i]) - a;
// 在 a[x-1] 之后来的选手中,截止时间最少是 t[i]+p[i]
nxt[x - 1] = min(nxt[x - 1], t[i] + p[i]);
}
// 后缀最小值:从后往前刷,使得 nxt[j] 代表所有 > a[j] 时刻到达选手的最小截止时间
for(int i = m - 1; i >= 0; i--) nxt[i] = min(nxt[i], nxt[i + 1]);
// DP 初始化
for(int i = 1; i <= m; i++) dp[i] = -1e18;
dp[1] = 0; // 0时刻在门口,开心度为0
int ptr = 1;
for(int i = 1; i <= m; i++) {
// 【转移1】Case 2:从 i-1 顺延一直待在门口(不需要单调队列)
if(i > 1 && dp[i - 1] > -1e17) {
dp[i] = max(dp[i], dp[i - 1] - (a[i] - a[i - 1]) * x00 + (sum_fp[i] - sum_fp[i - 1]));
}
// 【双指针插入】将满足沙发往返距离限制(a[i] - a[ptr] >= 2L)的决策点 ptr 放入队列
while(ptr <= m && a[ptr] <= a[i] - 2 * l) {
if(dp[ptr] > -1e17) {
int v = ptr;
k1[v] = sum_f[v];
b1[v] = dp[v] - a[v] * x11 - sum_ftp[v];
// 使用 __int128 避免斜率比较时乘法溢出
auto check = [&](int u, int w, int v) {
return (__int128)(b1[u] - b1[w]) * (k1[v] - k1[w]) >= (__int128)(b1[w] - b1[v]) * (k1[w] - k1[u]);
};
// 维护正常 DP 的上凸包
bool insert_s1 = true;
while(s1.size() >= 2) {
int w = s1.back(); s1.pop_back();
int u = s1.back();
if(!check(u, w, v)) {
s1.push_back(w);
break;
}
}
if(s1.size() == 1 && k1[s1.back()] == k1[v]) {
if(b1[v] >= b1[s1.back()]) s1.pop_back();
else insert_s1 = false;
}
if(insert_s1) s1.push_back(v);
// 维护用于统计最终答案的凸包(要求决策出发点 a[ptr] < max_t)
if(a[ptr] < max_t) {
bool insert_ans = true;
while(ans_s1.size() >= 2) {
int w = ans_s1.back(); ans_s1.pop_back();
int u = ans_s1.back();
if(!check(u, w, v)) {
ans_s1.push_back(w);
break;
}
}
if(ans_s1.size() == 1 && k1[ans_s1.back()] == k1[v]) {
if(b1[v] >= b1[ans_s1.back()]) ans_s1.pop_back();
else insert_ans = false;
}
if(insert_ans) ans_s1.push_back(v);
}
}
ptr++;
}
// 【转移2】从单调队列 s1 中寻找 Case 1 的最优解更新 dp[i]
// 1. 弹出超时过期的决策点
while(!s1.empty() && nxt[s1.front()] < a[i]) s1.pop_front();
// 2. 弹出非最优的队头
while(s1.size() >= 2) {
int u = s1[0], v = s1[1];
if(k1[u] * a[i] + b1[u] <= k1[v] * a[i] + b1[v]) s1.pop_front();
else break;
}
// 3. 更新当前 DP 值
if(!s1.empty()) {
int j = s1.front();
int val = k1[j] * a[i] + b1[j] + a[i] * x11 + sum_ftp[i] - a[i] * sum_f[i] - 2 * l * x11 - l * (x01 + x10);
dp[i] = max(dp[i], val);
}
// 【统计最终答案】
// 1. 如果刚好在最后一名选手到达时(且全部接待完)结束
if(a[i] == max_t) {
ans = max(ans, dp[i]);
}
// 2. 通过沙发往返并在 a[i] 处刚好接待完最后一名选手(从 ans_s1 队列中转移)
while(!ans_s1.empty() && nxt[ans_s1.front()] < a[i]) ans_s1.pop_front();
while(ans_s1.size() >= 2) {
int u = ans_s1[0], v = ans_s1[1];
if(k1[u] * a[i] + b1[u] <= k1[v] * a[i] + b1[v]) ans_s1.pop_front();
else break;
}
if(a[i] >= max_t && !ans_s1.empty()) {
int j = ans_s1.front();
int val = k1[j] * a[i] + b1[j] + a[i] * x11 + sum_ftp[i] - a[i] * sum_f[i] - 2 * l * x11 - l * (x01 + x10);
ans = max(ans, val);
}
}
cout << ans << '\n';
}
// 妈的这个沙比体我真的写不动了 aier 启动

浙公网安备 33010602011771号