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 这篇文章有借鉴

浙公网安备 33010602011771号