[FJWC2019] 全连
Description
有若干个音符,出现的时间为\(i\),若选择这个音符,则\((i-t_I,i+t_i)\)这部分的音符都不能选。每个音符都有权值,求可以得到的最大权值。数据范围\(N \leq 1000000\)。
Solution
上机的时候没有做出来。只写了一个\(O(N^2)\)的解。
一开始我想把这些音符建个图:所有音符向\((i-t_I,i+t_i)\)之外的音符连边,然后求一个最短路径。
仔细读题,由于时间是递进的,所以不能回去选,所以满足无后效性,可以考虑dp求解。
对于第\(i\)个音符,如果选择它并达到当前最优,那么一定是从\([1,i-t_i]\)中选取最优的转移过来。设\(f[i]\)表示选取\(i\)的时候的最优解,显然答案就是\(f[N]\)。
然后for循环一遍,判断两个音符之间是否会发生冲突,进行转移即可。
但是这样时间复杂度是\(O(N^2)\)的,不能通过这道题。
之后听学长讲之后恍然大悟:
寻找最优的转移过程不需要枚举,可以考虑线段树或者树状数组,维护\([1,i-t_i]\)的最值即可。
并且我们需要动态插入值,当\(j+t_j \leq i\)时,音符\(j\)绝对可以作为之后音符的前驱,所以将其值放入数据结构中进行维护。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 1000005
#define lowbit(x) (x&(-x))
struct Node {
int r; int idx;
bool operator < (const Node &x) const {
return r<x.r;
}
}q[MAXN];
long long a[MAXN],C[MAXN];
int t[MAXN];
int N,tail = 1;
inline long long query(int x) {
long long maxx = 0;
for(;x>0;x-=lowbit(x)) maxx = std::max(maxx,C[x]);
return maxx;
}
inline void update(int x,long long num) {
for(;x<=N;x+=lowbit(x)) C[x] = std::max(num,C[x]);
}
int main() {
scanf("%d",&N);
for(int i=1;i<=N;++i) scanf("%d",&t[i]);
for(int i=1;i<=N;++i) {
scanf("%lld",&a[i]); a[i] *= (long long)t[i];
q[i].idx = i; q[i].r = i + t[i];//转换成权值
}
std::sort(q+1,q+1+N);//按i+t[i]从小到大排序
long long ans = 0;
for(int i=1;i<=N;++i) {
while(tail<=N&&q[tail].r<=i)
update(q[tail].idx,a[q[tail].idx]),tail++;//不会对之后产生影响时,一个个插入
if(i-t[i]>0) a[i] += query(i-t[i]);//看看有没有可以转移的,否则直接从这个音符开始选
ans = std::max(a[i],ans);//记录答案
}
printf("%lld",ans);
return 0;
}