#4780. 病毒研究

题目描述

病毒科学家陈博士正在带领着她的团队在研究病毒,她要研究如何降低病毒的活性。病毒的活性可以用一个整数来表示,但她不知道具体的活性是多少。

她可以执行 $m+1$ 种操作,对于前 $m$ 种操作,第 $i$ 种操作为花费 $v_i$ 的代价使得病毒 的活性减少 $w_i$;第 $m+1$ 种操作为查看当前病毒所处的状态,不需要花费任何代价。

病毒一共有 $n$ 种状态。有 $n+1$ 个递增的数 $a_0,a_1,a_2,...,a_n$,其中 $a_0=0$;若病毒的活性 $x$ 满足 $a_{i-1}<x\le a_i$,那么这个病毒就处于状态 $i$。同时,保证病毒的活 性不会大于 $a_n$。

她可以使用每种操作任意多次,但是她不希望病毒完全丧失活性。但是如果在使用了一个操作后,病毒的活性 $\le 0$ 了,那研究就失败了。而病毒的活性太高时也不适合研究,只有病毒处于状态 $1$ 时才最适合研究。

现在,她只知道病毒的活性是 $[1,a_n]$ 中的一个等概率随机的整数。她想知道,在保证病毒不会完全丧失活性的情况下,她使病毒变为状态 $1$ 的过程中,花费代价的期望最少是多少。

可以发现答案乘 $a_n$ 一定是个整数,输出答案乘 $a_n$ 的值即可。

如果不能保证病毒不会完全丧失活性,输出 $-1$。

数据范围

$1\le a_1 < a_2 < ... < a_n \le 2000 , 1\le T \le 10 , 1 \le v_i \le 10^6$ 。

题解

考虑 $\text{dp}$ : $f[l][r]$ 表示处于 $[l,r]$ 中的数到达 $1$ 状态的期望总和。考虑 $l,r$ 如果不在一个状态就是几个 $\text{dp}$ 相加,如果在一个状态里的话,就向前平移并且加上向前平移的代价,这样是 $O(n^2m)$ 的。

考虑减少状态数,我们可以假设一直平移到第一次分割的位置,那么中间这个过程就可以用背包来实现。那我们就只需要记 $f[a_{i-1}+1][x]$和 $f[x][a_i]$ 这样的状态,其中 $x$ 处于 $i$ 状态中。这样效率为 $O(nm)$ 。

代码

 

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2005;
const LL G=1e18;
int T,n,m,z,a[N],b[N];
LL f[N][N],g[N],s[N];
LL F(int l,int r){
    if (f[l][r]!=G) return f[l][r];
    for (int x,y,i=1;i<l;i++)
        if (g[i]!=G && (b[l-i]!=b[r-i] || r-i<=a[1])){
            x=l-i;y=r-i;
            LL u=g[i]*(r-l+1)+F(x,a[b[x]])+F(a[b[y]-1]+1,y);
            if (b[x]+1<b[y]) u+=s[b[y]-1]-s[b[x]];
            f[l][r]=min(u,f[l][r]);
        }
    return f[l][r];
}
void work(){
    scanf("%d%d",&n,&m);
    memset(b,0,sizeof b);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]),
        b[a[i]+1]++;z=a[n];b[0]=1;
    for (int i=1;i<=z;i++)
        b[i]+=b[i-1],g[i]=G;
    for (int x,y;m--;){
        scanf("%d%d",&x,&y);
        for (int i=y;i<=z;i++)
            g[i]=min(g[i],g[i-y]+x);
    }
    for (int i=1;i<=z;i++)
        for (int j=i;j<=z;j++) f[i][j]=G;
    for (int i=1;i<=a[1];i++)
        for (int j=i;j<=a[1];j++) f[i][j]=0;
    for (int x,y,i=2;i<=n;i++){
        x=a[i-1]+1;y=a[i];
        for (int r=x;r<=y;r++) f[x][r]=F(x,r);
        for (int l=x;l<y;l++) f[l][y]=F(l,y);
        if (f[x][y]==G){puts("-1");return;}
        s[i]=s[i-1]+f[x][y];
    }
    printf("%lld\n",s[n]);
}
int main(){for (cin>>T;T--;work());return 0;}

 

posted @ 2020-04-18 18:38  xjqxjq  阅读(175)  评论(0编辑  收藏  举报