题目描述
病毒科学家陈博士正在带领着她的团队在研究病毒,她要研究如何降低病毒的活性。病毒的活性可以用一个整数来表示,但她不知道具体的活性是多少。
她可以执行 $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}<xle a_i$,那么这个病毒就处于状态 $i$。同时,保证病毒的活 性不会大于 $a_n$。
她可以使用每种操作任意多次,但是她不希望病毒完全丧失活性。但是如果在使用了一个操作后,病毒的活性 $le 0$ 了,那研究就失败了。而病毒的活性太高时也不适合研究,只有病毒处于状态 $1$ 时才最适合研究。
现在,她只知道病毒的活性是 $[1,a_n]$ 中的一个等概率随机的整数。她想知道,在保证病毒不会完全丧失活性的情况下,她使病毒变为状态 $1$ 的过程中,花费代价的期望最少是多少。
可以发现答案乘 $a_n$ 一定是个整数,输出答案乘 $a_n$ 的值即可。
如果不能保证病毒不会完全丧失活性,输出 $-1$。
数据范围
$1le a_1 < a_2 < ... < a_n le 2000 , 1le T le 10 , 1 le v_i le 10^6$ 。
题解
考虑 $ ext{dp}$ : $f[l][r]$ 表示处于 $[l,r]$ 中的数到达 $1$ 状态的期望总和。考虑 $l,r$ 如果不在一个状态就是几个 $ ext{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 ",s[n]); } int main(){for (cin>>T;T--;work());return 0;}