[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;
}

猜你喜欢

转载自www.cnblogs.com/Neworld2002/p/10389442.html