[CodeForces-708E]Student's Camp

题目大意:
  一个n*m的墙,被吹k天风,每块靠边的砖都有p的概率被吹掉。
  如果上下两行没有直接相连的地方,我们则认为这一堵墙已经倒塌。
  问最后墙不倒塌的概率(模意义)。

思路:
  动态规划。
  用f[i][j][k]表示到了第i层,只剩下j~k的砖头并且不倒塌的概率。
  则f[i][j][k]=sum{f[i-1][l][r]|[l,r]与[j,k]有交集}*这一层只剩[l,r]的概率。
  概率可以O(n)预处理,接下来要枚举i,j,k,l,r,所以是O(m^4n)的。
  接下来考虑预处理sum{f[i-1][l][r]|[l,r]与[j,k]有交集}。
  显然有交集的概率=总概率-没有交集的概率=总概率-r<i的概率-j<l的概率。
  而这些概率都可以一边转移一边推。
  这样转移的时候就不需要考虑具体的l,r,是O(m^2n)的。
  数组1500^3还会爆,考虑滚动数组,勉强开下,反正还是TLE。
  正解是一个很神奇的O(mn)算法。(并不是很懂)
  考虑用f[i][k]表示前面的f[i][1][k]~f[i][k][k]的和。
  然后预处理所有关于j的前缀和。
  然后递推式就只与i,k有关了。

 1 #include<cstdio>
 2 #include<cctype>
 3 typedef long long int64;
 4 inline int getint() {
 5     register char ch;
 6     while(!isdigit(ch=getchar()));
 7     register int x=ch^'0';
 8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 9     return x;
10 }
11 const int mod=1e9+7;
12 const int N=1501,M=1501,K=100001;
13 void exgcd(const int &a,const int &b,int &x,int &y) {
14     if(!b) {
15         x=1;
16         y=0;
17         return;
18     }
19     exgcd(b,a%b,y,x);
20     y-=a/b*x;
21 }
22 inline int inv(const int64 &x) {
23     int tmp,ret;
24     exgcd(x,mod,ret,tmp);
25     return (ret+mod)%mod;
26 }
27 int f[2][M];
28 int p[K],q[K];
29 int fact[K],tcaf[K];
30 int k;
31 inline int calc(const int &x) {
32     return (int64)fact[k]*tcaf[k-x]%mod*tcaf[x]%mod*p[x]%mod*q[k-x]%mod;
33 }
34 int main() {
35     int n=getint(),m=getint();
36     int a=getint(),b=getint();
37     k=getint();
38     fact[0]=1;
39     for(register int i=1;i<=k;i++) {
40         fact[i]=(int64)fact[i-1]*i%mod;
41     }
42     tcaf[k]=inv(fact[k]);
43     for(register int i=k-1;~i;i--) {
44         tcaf[i]=(int64)tcaf[i+1]*(i+1)%mod;
45     }
46     p[0]=q[0]=1;
47     p[1]=q[1]=inv(b);
48     p[1]=(int64)p[1]*a%mod;
49     q[1]=(int64)q[1]*(b-a)%mod; 
50     for(register int i=2;i<=k;i++) {
51         p[i]=(int64)p[i-1]*p[1]%mod;
52         q[i]=(int64)q[i-1]*q[1]%mod;
53     }
54     f[0][m]=1;
55     for(register int i=1;i<=n;i++) {
56         int s1=0,s2=0;
57         for(register int j=0;j<=m;j++) {
58             f[i&1][j]=((int64)s1*(f[!(i&1)][m]-f[!(i&1)][m-j])-s2)%mod*calc(m-j)%mod;
59             s1=(s1+calc(j))%mod;
60             s2=(s2+(int64)f[!(i&1)][j]*calc(j))%mod;
61         }
62         for(register int j=1;j<=m;j++) {
63             f[i&1][j]=(f[i&1][j]+f[i&1][j-1])%mod;
64         }
65     }
66     printf("%d\n",(f[n&1][m]+mod)%mod);
67     return 0;
68 }

 

posted @ 2017-10-17 09:05  skylee03  阅读(170)  评论(0编辑  收藏  举报