古典叙事 · 技术札记

序章

山河有卷
人间有声

写代码,也写长风、旧城与未熄的灯。
愿每一篇随笔,都有自己的山水与回声。
阅览随笔

The 2022 ICPC Asia Nanjing Regional Contest B

题意:
现在有0到n+1个点,这些点两两相邻,并且每一个点都有一个点权,现在要求第一个点0和最后一个点n+1必须要选择,再给出一个长度为n的01串,第i个字符代表第编号为i的点是否要选择,现要求任意两个点的距离都要小于给定的数字k,再给你m次修改,可以将点x的点权改变为num,现在要求你输出,每次修改后能选出的最小点权是多少

# 思路

首先这是一个dp问题,dp[i]代表第i个点被选择需要的最小权值,那么dp[i]会从dp[j]转移而来,j=[i-k,i-1];我们可以用单调队列优化,降低复杂度。
现在的问题是,多了一个临时修改的点,对于这一个点,我们从他左边k个开始重新转移,转移到他右边k个点这一段,通过从右往左预处理好从第n+1个选到第j个需要的最小权,然后就可以通过枚举计算答案了

   for (i64 i = p; i <= min(n + 1,p+k-1); i++) {
while (head <= tail && i - q[head] > k) head++;//保持单调队列
f[i] = f[q[head]] + a[i];//更新当前节点的最小值
res = min(res, f[i] + g[i]);//更新答案
if (s[i] == '1')head = 0, tail = -1;
while (head <= tail && f[q[tail]] >= f[i]) tail--;
q[++tail] = i;
}
通过代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <string>
#include <cstring>
using namespace std;
using i64 = long long;
#define endl '\n';
i64 t;
void solve() {
    i64 n, k;
    cin >> n >> k;
    vector<i64> a(n+10), f(n+10),g(n+10),q(n+10);
    for (i64 i = 1; i <= n; i++) {
    cin >> a[i];
    }
    string s;
    cin >> s;
    s = " " + s;
    i64 head = 0, tail = -1;
    for (int i = 0; i <= n + 1; i++)
    {
        while(head <= tail && i - q[head] > k) head++;
        f[i] = f[q[head]] + a[i];
        if (s[i] == '1')head = 0, tail = -1;
        while (head <= tail && f[q[tail]] >= f[i]) tail--;
        q[++tail] = i;
    }
    head = 0, tail = -1;
    for (int i = n + 1; i >= 0; i--)
    {
        while (head <= tail && q[head] - i > k) head++;
        g[i] = g[q[head]] + a[i];
        if (s[i] == '1')head = 0, tail = -1;
        while (head <= tail && g[q[tail]] >= g[i]) tail--;
        q[++tail] = i;
    }
    for (i64 i = 0; i <= n + 1; i++) {
        g[i] -=a[i];
    }
    i64 m;
    cin >> m;
    vector<i64> F = f;
    while (m--) {
        i64 p, v;
        cin >> p >> v;
        i64 tmp=a[p];
        a[p] = v;
        head=0, tail = -1;
        for (int i = max(i64(0), p - k); i < p; i++)
        {
            while (head <= tail && i - q[head] > k) head++;
            if (s[i] == '1')head = 0, tail = -1;
            while (head <= tail && f[q[tail]] >= f[i]) tail--;
            q[++tail] = i;
        }
        for (int i = p; i <= min(n + 1, p + k - 1); i++)
        {
            F[i] = f[i];
        }
        i64 res = 1e18;
        for (i64 i = p; i <= min(n + 1,p+k-1); i++) {
        while (head <= tail && i - q[head] > k) head++;//保持单调队列
        f[i] = f[q[head]] + a[i];//更新当前节点的最小值
        res = min(res, f[i] + g[i]);//更新答案
        if (s[i] == '1')head = 0, tail = -1;
        while (head <= tail && f[q[tail]] >= f[i]) tail--;
        q[++tail] = i;
        }
        cout << res << endl;
        for (int i = p; i <= min(n + 1, p + k - 1); i++)
        {
            f[i] = F[i];
        }
        a[p] = tmp;
    }
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;

}

对于https://blog.csdn.net/weixin_73725403/article/details/142098082 这篇文章有借鉴

posted @ 2025-10-13 15:53  Morphis‘  阅读(14)  评论(0)    收藏  举报

特别策划 · CINEMATIC NOTES

风沙与孤骑

风起塞外,
胜负在刀剑之前。

“把复杂拆成秩序,把未知写成答案。”
Morphis · 山河一卷 愿你从这里出发,仍能听见山风。 影像:farfarSébastien Goldberg / Unsplash