2024.1.4做题纪要

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
*/

P3195 [HNOI2008] 玩具装箱

先列出 \(dp\) 式子:

\[dp[i] = \min_{j=0}^{i - 1} \{dp[j] + (i - (j + 1) + pre_i - pre_j - L)^2\} \]

单独考虑 \(\min\) 里面的式子。

我们设 \(sum_i=pre_i + i\)

则式子变成:

\[dp_j+ (sum_i - sum_j - 1 - L) ^ 2 \]

我们再将 \(L\) 提前加上 \(1\)

\[dp_j + (sum_i - sum_j - L) ^ 2 \]

开平方为

\[sum_i \cdot (sum_i - sum_j - L) - sum_j \cdot (sum_i - sum_j - L) - L \cdot (sum_i - sum_j - L) \]

\[sum_i^2 - sum_i \cdot sum_j - sum_i \cdot L - sum_i \cdot sum_j + sum_j^2 + sum_j \cdot L - sum_i \cdot L + sum_j \cdot L + L^2 \]

归类:

\[sum_i^2 - sum_i \cdot L - sum_i \cdot L+ L^2 - sum_i \cdot sum_j - sum_i \cdot sum_j + sum_j^2 + sum_j \cdot L + sum_j \cdot L \]

\[(sum_i - L) ^ 2 + sum_j^2 + 2 \cdot sum_j \cdot (L - sum_i) \]

\((sum_i - L) ^ 2\) 提出,就成了下列式子:

\[sum_j^2 + 2 \cdot sum_j \cdot (L - sum_i) \]

我们设 \(-k = 2 \cdot (L - sum_i)\),则式子变成了:

\[f(j)=dp_j+sum_j^2 - k \cdot sum_j \]

我们设 \(j_1 < j_2\)\(f(j_1) \ge f(j_2)\), 此时我们就会选 \(j_2\),那么:

\[dp_{j_1} + sum_{j_1}^2 - k \cdot sum_{j_1} \ge dp_{j_2} + sum_{j_2}^2 - k \cdot sum_{j_2} \]

\[dp_{j_1} + sum_{j_1}^2 - (dp_{j_2} + sum_{j_2}^2) \ge k \cdot (sum_{j_1} - sum_{j_2}) \]

因为 \(sum_{j_1} - sum_{j_2}\) 一定大于 \(0\),所以当不为 \(0\) 的时候

\[\frac{dp_{j_1} + sum_{j_1}^2 - (dp_{j_2} + sum_{j_2}^2)}{sum_{j_1} - sum_{j_2}} \le k \]

\[\frac{dp_{j_2} + sum_{j_2}^2 - (dp_{j_1} + sum_{j_1}^2)}{sum_{j_2} - sum_{j_1}} \le k \]

激动
#include <bits/stdc++.h>

typedef long long ll;

int N;
ll L, dp[51000], C[51000], sum[51000];

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

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

    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 emplace(ll x, ll y) {
        std::pair<ll, ll> result = std::make_pair(x, y);
        while (l + 1 <= r && greater(number[r - 1], number[r], result)) 
            r --;
        number[++ r] = result;
    }

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

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

    void print() {
        for (int i = l; i + 1 <= r; ++ i)
            std::cout << Slope(number[i], number[i + 1]) << ' ';
        std::cout << '\n';
    }
}queue;

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

    std::cin >> N >> L;
    L ++;
    for (int i = 1; i <= N; ++ i) {
        std::cin >> C[i];
        sum[i] = sum[i - 1] + C[i];
    }
    for (int i = 1; i <= N; ++ i)
        sum[i] += i;
    queue.emplace(0, 0);
    for (int i = 1; i <= N; ++ i) {
        int k = 2ll * (sum[i] - L);
        queue.pop(k);
        std::pair<ll, ll> temp = queue.front();
        dp[i] = temp.second - 1ll * k * temp.first + (sum[i] - L) * (sum[i] - L);
        queue.emplace(sum[i], dp[i] + sum[i] * sum[i]);
    }
    std::cout << dp[N] << '\n';
    return 0;
}

P2120 [ZJOI2007] 仓库建设

其实跟上面的一样,不多BB了。但是该注意的是 \(p_i=0\) 的情况,当 \(p_i=0\) 时直接 \(dp_i=dp_{i-1}\),再与单调队列求出来的取 \(\min\) 就行了,其实一些数组不太一样,明天再说。

兴奋
#include <bits/stdc++.h>

typedef long long ll;

int N;
ll p[1100000], x[1100000], c[1100000];
ll partSum[1100000], costSum[1100000];
ll dp[1100000];

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

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

    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 emplace(ll x, ll y) {
        std::pair<ll, ll> result = std::make_pair(x, y);
        while (l + 1 <= r && greater(number[r - 1], number[r], result)) 
            r --;
        number[++ r] = result;
    }

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

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

    void print() {
        for (int i = l; i + 1 <= r; ++ i)
            std::cout << Slope(number[i], number[i + 1]) << ' ';
        std::cout << '\n';
    }
}queue;

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

    std::cin >> N;
    for (int i = 1; i <= N; ++ i) {
        std::cin >> x[i] >> p[i] >> c[i];
        partSum[i] = partSum[i - 1] + p[i];
        dp[i] = 2e18;
    }
    for (int i = N - 1; i >= 0; -- i) {
        costSum[i] = costSum[i + 1] + (x[N] - x[i + 1]) * p[i + 1];
    }
    dp[0] = 0;
    queue.emplace(partSum[0], dp[0] + costSum[0]);
    for (int i = 1; i <= N; ++ i) {
        ll k = x[N] - x[i];
        queue.pop(-k);
        std::pair<ll, ll> temp = queue.front();
        if (p[i] == 0)
            dp[i] = dp[i - 1];
        dp[i] = std::min(dp[i], temp.second + k * temp.first - costSum[i] - k * partSum[i] + c[i]);
        queue.emplace(partSum[i], dp[i] + costSum[i]);
    }
    std::cout << dp[N] << '\n';
    return 0;
}
posted @ 2024-01-04 16:44  觉清风  阅读(25)  评论(0编辑  收藏  举报