题目
link
题解
考虑dp
设f[i]
表示使序列由全是1到满足集合P条件所需的最少代价
然后想一想,f[i]
怎么推
首先,我们可以把p[i]
之前的全部推平,都变成0,然后一个一个变成1
其次,我们也可以把f[i - 1]
搞出来,然后把p[i - 1] + 1
到p[i] - 1
之间都弄成0。
这里就只能进行若干次第二次
操作了,可以用前缀和来优化
但是“首先”一条的复杂度这么做肯定太高,因此再设g[i]
表示使序列由全是0到满足集合P条件所需的最少代价。
状态转移方程在代码里(
锅
考场上想到有可能要用dp,但没接着想下去。而是想贪心性质去了(但是好像可以用贪心做,等几天看看别的题解)
还真有
https://www.luogu.com.cn/blog/Pretharp/p9744-ti-xie
继而可得,我们需要归零的前缀的末尾必然是在某一个将要变为 1 的位置的前面一个位置,显然可以直接对于每个询问 O(m) 枚举得到答案。时间复杂度 O(n+mq)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<bits/stdc++.h> const int N = 5e5 + 10; using namespace std; #define int long long int a[N], b[N], c[N]; int n;
int f[N], g[N];
int p[N], m; int b_sum[N]; signed main() { scanf("%lld", &n); for(int i = 1;i <= n;i++) scanf("%lld", &a[i]); for(int i = 1;i <= n;i++) scanf("%lld", &b[i]), b_sum[i] = b_sum[i - 1] + b[i]; for(int i = 1;i <= n;i++) scanf("%lld", &c[i]);
for(int i = 2;i <= n;i++) a[i] = min(a[i - 1] + b[i], a[i]); int q; scanf("%lld", &q);
while(q --) { scanf("%lld", &m); for(int i = 1;i <= m;i++) { scanf("%lld", &p[i]); }
for(int i = 1;i <= m;i++) { g[i] = g[i - 1] + c[p[i]]; f[i] = a[p[i] - 1] + g[i - 1]; f[i] = min(f[i], f[i - 1] + b_sum[p[i] - 1] - b_sum[p[i - 1]]); } printf("%lld\n", min(g[m] + a[n], f[m] + b_sum[n] - b_sum[p[m]])); } return 0; }
|