2026.7.7 考试总结

赤石总结

估:100 + 100 + 100 + 30

得:100 + 40 + 100 + 45 rk2

前提紧要

你说的对,但这就是牛客。这 t4 真的是黑吧,调了一下午一晚上都弄不出来,还是 Gemini 强啊。妈的沙比出题人诗人啊,T2 神人贪心优化搜索,T4 沙比斜率优化,算了不多喷了。

link

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)\) 拆开得到:

\[(a + c - 2b) ^ 2 = a ^ 2 + 4 b ^ 2 + c ^ 2 + 2 a c - 4 a b - 4 b c \]

然后这个就一个一个可以维护了。树剖,每个点都维护一下自己子树内、不包括重儿子的子树内的信息,开 \(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\),求其最大的贡献,那么有两种,一是要去沙发,二是一直在门口带着,那么有:

\[calc(x) = \max(-l (x_{01} + x_{10}) + (x - 2l) \times x_{11}, -x \times x_{00}) \]

首先可以有 \(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 启动
posted @ 2026-07-07 21:37  ACehomoxue  阅读(7)  评论(0)    收藏  举报