2024.1.4做题纪要

发布时间 2024-01-04 16:46:42作者: 觉清风

P3628 [APIO2010] 特别行动队

斜率优化板子题。具体式子不说了QAQ。

记住当让 \(\frac{y_1 - y_2}{x_1 - x_2} \ge k\) 时维护上凸壳,当 \(\frac{y_1 - y_2}{x_1 - x_2} \leq k\) 时维护下凸壳。

还有当化简 \(dp\) 式子的时候,把纯常量提出来,只存纯变量,将与变量相乘的常量作为 \(k\)

开心
#include <bits/stdc++.h>

typedef long long ll;

int N;
ll a, b, c;
ll value[1010000];
ll dp[1001000], sum[1001000];
// ll queue[1001000], l = 1, r = 0;

ll GetValue(ll val) {
    return a * val * val + b * val + c;
}

double Slope(std::pair<ll, ll> &a, std::pair<ll, ll> &b) {
    return 1.0 * (b.second - a.second) / (1.0 * (b.first - a.first));
}

class Monotonic_Queue {
public:
    std::pair<ll, ll> number[1001000];
    int id[1001000];
    int l, r;
    
    Monotonic_Queue() {
        l = 1, r = 0;
    }


    bool less(std::pair<ll, ll> &a, std::pair<ll, ll> &b, std::pair<ll, ll> &c) {
        return Slope(a, b) <= Slope(b, c);
    }

    bool greater(std::pair<ll, ll> &a, std::pair<ll, ll> &b, std::pair<ll, ll> &c) {
        return Slope(a, b) >= Slope(b, c);
    }

    void pop(double k) {
        while (l + 1 <= r && k < Slope(number[l], number[l + 1]))
            l ++;
    }

    void emplace(ll x, ll y, int ID) {
        std::pair<ll, ll> New = std::make_pair(x, y);
        while (l + 1 <= r && less(number[r - 1], number[r], New)) 
            r --;
        id[++ r] = ID;
        number[r] = New;
    }

    std::pair<ll, ll> front_pos() {
        return number[l];
    }

    int front_id() {
        return id[l];
    }
}queue;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    std::cin >> N;
    std::cin >> a >> b >> c;
    for (int i = 1; i <= N; ++ i) {
        std::cin >> value[i];
        sum[i] = sum[i - 1] + value[i];
    }
    queue.emplace(0, 0, 0);
    for (int i = 1; i <= N; ++ i) {
        double k = 2 * a * sum[i];
        queue.pop(k);
        int j = queue.front_id();
        std::pair<ll, ll> temp = queue.front_pos();
        dp[i] = temp.second - 2ll * a * sum[i] * sum[j] + 1ll * a * sum[i] * sum[i] + 1ll * b * sum[i] + c;
        queue.emplace(sum[i], dp[i] + 1ll * a * sum[i] * sum[i] - 1ll * b * sum[i], i);
    }
    std::cout << dp[N] << '\n';
    return 0;   
}

省选联测6 耀眼

逆天根号分治。

不会讲,太菜了QAQ。

伤心
// ubsan: undefined
// accoders
#include <bits/stdc++.h>

typedef long long ll;
const ll mod = 998244353;
const int blocks = 650;
const int S = 1023;

ll N, W;
int dp1[S + 10][1400], dp2[2][400100];

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

#ifndef LOCAL_STDIO
    freopen("dazzling.in", "r", stdin);
    freopen("dazzling.out", "w", stdout);
#endif

    std::cin >> N >> W;

    if (N == 1) {
        std::cout << 0 << '\n';
        return 0;
    }

    for (int i = 1; i <= N; ++i) {
        memset(dp1[i & S], 0, sizeof(dp1[i & S]));

        if (i <= blocks)
            dp1[i][i] = 1;

        for (int j = 1; j <= 1000 && j <= i; ++j)
            dp1[i & S][j] = (dp1[i & S][j] + W * dp1[(i - j) & S][j + 1] + dp1[(i - j) & S][j - 1]) % mod;
    }

    ll answer = 0;
    for (int i = 1; i <= 1000; ++i)
        answer = (answer + dp1[N & S][i]);

    dp2[0][N] = 1;
    bool flag = 1;
    // answer = 0;
    for (int i = 1; i * blocks <= 2 * N; ++i) {
        memset(dp2[flag], 0, sizeof(int) * (N * 2));
        for (int j = 0; j <= 2 * N; ++j) {
            dp2[flag][j] = (dp2[flag][j] + W * (i + j <= 2 * N ? dp2[flag ^ 1][j + i] : 0) + (j - i >= 0 ? dp2[flag ^ 1][j - i] : 0)) % mod;
            //注意从flag转移过来的
        }
        for (int j = blocks + 1; j <= N; ++j)
            if (i * j <= 2 * N)
                answer = (answer + dp2[flag ^ 1][N * 2 - i * j]);
        //这里加 dp2[flag ^ 1] 的原因是:在当前这一位之后,剩下还要选的长度是flag^1滚之前的代表长度
        flag ^= 1;
    }

    answer %= mod;

    std::cout << answer << '\n';
    return 0;
}
/*
5 2
*/