首页 > 其他分享 >2022ICPC南京站 B. Ropeway

2022ICPC南京站 B. Ropeway

时间:2023-08-05 18:55:58浏览次数:56  
标签:南京站 ch ++ Ropeway -- while 2022ICPC ed hd

也许更好的阅读体验

\(\mathcal{Description}\)
\(n+2\)个点编号\(0~n+1\),每个点有点权,要求选若干个点使得总点权最小,其中编号为\(0\)和\(n+1\)的点必须选且点权为\(0\),同时满足任意两个被选的点之间的距离不超过\(k\),此外还会给一个\(01\)串,表示\(1~n\)这些点是否为必选的点
现在会给\(m\)个询问,每个询问为如果将编号为\(x\)的点权修改为\(v\)的答案会是什么
多组数据
\(n\le 5× 10^5,\sum n\le 2×10^6,1\le m, k\le 3×10^3,\sum m=\sum k\le10^4\)

\(\mathcal{Solution}\)
最近做做往年\(ICPC\)题,这题算是个金牌题,做的快可以摸金的那种,意外地发现很简单
没有修改的话是个很经典的单调栈优化\(DP\)
设\(f_i\)表示\([0,i]\)这个区间满足条件且在i选了i的最小花费,转移方程为\(f_i=min\{f_j\}\),这个过程单调栈优化可以做到\(O(n)\),其中\(j\)属于\([i-k,i-1]\)
对于某个位置必须选择,那么就在计算该位置的\(dp\)值后将单调栈清空再将该位置放进去,相当于之后的选择一定有这个位置了
正着做和反着做差不多,再设\(g[i]\)表示满足\([i,n+1]\)这个区间的最小花费,则答案就是\(min\{f_i+min\{g_j\}\}\)
考虑修改某个位置的值,因为任意一个长度为\(k\)的区间一定有一个必须选,因此当修改一个位置的值时,只要从那个位置开始往后走\(k\)步算一次\(f_i+min\{g_j\}\)就可以算出答案,这样复杂度是\(O(mk)\),是满足题意的
现在问题就是怎么在修改后\(O(1)\)的算出\(f_i\),首先考虑到如果把所有到\(i\)的单调栈存下来,修改了\(i\)位置后就从这个单调栈重新做一次,但是这样时空都不满足要求,为了解决这个问题,可以将询问都排序,这样就可以共用那个单调栈了

\(\mathcal{Code}\)

#include <cstdio>
#include <algorithm>
#define ll long long
#define inf 112345678901234567
using namespace std;
const int maxn = 5e5 + 10;
struct IO {
	template <class T>
	IO operator>>(T &res) {
		res = 0;
		char ch;
		bool flag = false;
		while ((ch = getchar()) > '9' || ch < '0')	flag |= ch == '-';
		while (ch >= '0' && ch <= '9')	res = (res << 3) + (res << 1) + (ch ^ '0'), ch = getchar();
		if (flag)	res = ~res + 1;
		return *this;
	}
} cin;
int T, n, m, k, hd, ed;
int a[maxn], q[maxn], q2[maxn], fr[maxn], x[maxn], v[maxn], id[maxn];
ll f[maxn], tf[maxn], g[maxn], ans[maxn];
char s[maxn];
bool cmp (int a, int b) { return x[a] < x[b]; }
int main ()
{
    cin >> T;
    while (T--) {
        cin >> n >> k;
        for (int i = 1; i <= n; ++i)    cin >> a[i];
        scanf("%s", s + 1);
        a[0] = a[n + 1] = f[0] = 0;
        q[hd = ed = 1] = 0;
        for (int i = 1; i <= n + 1; ++i) {
            while (q[hd] < i - k)   ++hd;
            f[i] = f[q[hd]] + a[i];
            if (s[i] == '1')    q[hd = ed = 1] = i;
            else {
                while (ed >= hd && f[q[ed]] >= f[i]) --ed;
                q[++ed] = i;
            }
        }
        q[hd = ed = 1] = n + 1, fr[n + 1] = n + 1, g[n + 1] = 0;
        for (int i = n; i >= 0; --i) {
            while (q[hd] > i + k)   ++hd;
            g[i] = g[q[hd]] + a[i];
            fr[i] = q[hd];
            if (s[i] == '1')    q[hd = ed = 1] = i;
            else {
                while (ed >= hd && g[q[ed]] >= g[i]) --ed;
                q[++ed] = i;
            }
        }
        cin >> m;
        for (int i = 1; i <= m; ++i)    scanf("%d%d", &x[i], &v[i]), id[i] = i, ans[i] = inf;
        sort(id + 1, id + m + 1, cmp);
        q[hd = ed = 1] = 0;
        int cur = 1;
        for (int i = 1; i <= n; ++i) {
            while (q[hd] < i - k)   ++hd;
            while (x[id[cur]] == i && cur <= m) {
                int hd2 = hd, ed2 = ed, t = a[i];
                a[i] = v[id[cur]];
                for (int j = hd; j <= ed; ++j)  q2[j] = q[j];
                int mx = min(i + k, n + 1);
                for (int j = i; j <= mx; ++j) {
                    while (q2[hd2] < j - k)   ++hd2;
                    tf[j] = tf[q2[hd2]] + a[j];
                    ans[id[cur]] = min(ans[id[cur]], tf[j] + g[fr[j]]);
                    if (s[j] == '1')    q2[hd2 = ed2 = 1] = j;
                    else {
                        while (ed2 >= hd2 && tf[q2[ed2]] > tf[j]) --ed2;
                        q2[++ed2] = j;
                    }
                }
                a[i] = t;
                ++cur;
            }
            tf[i] = tf[q[hd]] + a[i];
            if (s[i] == '1')    q[hd = ed = 1] = i;
            else {
                while (ed >= hd && f[q[ed]] > f[i]) --ed;
                q[++ed] = i;
            }
        }
        for (int i = 1; i <= m; ++i)    printf("%lld\n", ans[i]);
    }
    return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

标签:南京站,ch,++,Ropeway,--,while,2022ICPC,ed,hd
From: https://www.cnblogs.com/Morning-Glory/p/17608411.html

相关文章

  • 2022ICPC杭州站 A (裴蜀 + 扩欧)
    题目链接:A题意:给定一个序列\(a\),让序列加上一个等差序列,求出总和%$m$的最小值以及等差序列的\(s\)和公差\(d\)。思路:定义\(a\)序列总和为sum。则求解的答案为\((sum+n∗s+n∗(n+1)2∗d)\)%m的最小值。根据裴蜀定理得到原式等于\(sum+x∗gcd(n,n∗(n+1)/2)+y......
  • 2022icpc ec-final 游了记
    目录3.23day-13.24day03.25day13.26day23.27day3承上NOI2021退役记(密码123456):https://www.cnblogs.com/gmh77/p/15079696.html老年人的大学生活3.23day-1下午鸽......
  • 2022icpc 南京
    G.Inscryption贪心回溯在题目中0可以变成策略1,也可以变成策略2,但策略2是比策略1更加优秀的策略,所以当遇到0时能变成策略2就变成策略2,但是变成策略2可能会让后面的决策......
  • ICPC2022南京站游记
    第二次打南京了,去年是在南京拿的第一块铜(上海太卷了打了次铁)Day0南京站的热身赛真就万年不变,一直用那套袋鼠题。Day1开局我直接先敲板子,试图跟榜秒杀签到题,不久后\(I\)......
  • 2022icpc杭州铜牌题题解
    A.ModuloRuinstheLegend\[求s、d,使\suma_i+sn+d\frac{n(n+1)}{2}\(\bmodm)最小\\设sum=\suma_i\(\bmodm),t=gcd(n,\frac{n(n+1)}{2})\\原式=sum+kt\(\bm......
  • 2022icpc杭州(The 2022 ICPC Asia Hangzhou Regional Programming Contest)
    链接:https://codeforces.com/gym/104090A.ModuloRuinstheLegend#include"bits/stdc++.h"usingnamespacestd;usingi64=longlong;i64exgcd(i64a,i64b,......
  • 2022icpc西安(The 2022 ICPC Asia Xian Regional Contest)
    C#include"bits/stdc++.h"usingnamespacestd;usingi64=longlong;voidsolve(){i64a,b,c;cin>>a>>b>>c;i64tmp=1;i64ans=c*b;......
  • 2021icpc(银川)B (2022icpc沈阳热身赛) The Great Wall
    题意:给你一个长度为n的序列。对于每一个k,k∈[1,n].问你将其分成k个段,每个段的贡献为该段最大值-最小值。贡献总和最大值是多少.n≤1e3分析:很好写出一个朴素的dpdp[i......
  • 2022ICPC沈阳站游记
    这赛季的第一场,也是和新队友wushi和jcccc的第一场正式赛。赛前对这场比赛充满了期待,早已跃跃欲试。热身赛A题问主办方东北大学成立的时间在几月,这哪知道啊。但是热身......
  • 2022ICPC西安 游记
    离散课上闲来无事谢谢游记...吧游个锤子呀,还是熟悉的305一开始看L,感觉不像签到,去看J,瞪了半天没看出来,把题意给Leven一讲,Leven没多久就发现了最多选两个数,啊这。然后打了......