HDU - 5819

我们考虑每个向左的点, 从左往右进行转移。

f[ i ][ j ] 表示前 i 个点攻击完之后, 剩下 j 个点向右移动的概率。

考虑转移

f[ i ][ j ] = sum(f[ i - 1 ][ k ] * 2 ^ (k - j - 1))   k >= j

f[ i ][ 1 ] += sum(f[ i - 1 ][ j ] * 2 ^ (-j)

第一个转移显然可以优化到o(1)

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define ull unsigned long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define fio ios::sync_with_stdio(false); cin.tie(0)

using namespace std;

const int N = 1e3 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = (int)1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

template<class T, class S> inline void add(T &a, S b) {a += b; if(a >= mod) a -= mod;}
template<class T, class S> inline void sub(T &a, S b) {a -= b; if(a < 0) a += mod;}
template<class T, class S> inline bool chkmax(T &a, S b) {return a < b ? a = b, true : false;}
template<class T, class S> inline bool chkmin(T &a, S b) {return a > b ? a = b, true : false;}

int n;
int inv2 = mod / 2 + 1;
int ibin[N];
int bin[N];
int a[N];

int f[N][N];
int g[N][N];
int dp[N];

int power(int a, int b) {
    int ans = 1;
    while(b) {
        if(b & 1) ans = 1LL * ans * a % mod;
        a = 1LL * a * a % mod; b >>= 1;
    }
    return ans;
}


int main() {
    for(int i = ibin[0] = bin[0] = 1; i < N; i++) {
        ibin[i] = 1LL * ibin[i - 1] * inv2 % mod;
        bin[i] = 1LL * bin[i - 1] * 2 % mod;
    }
    int T, cas = 0;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                f[i][j] = g[i][j] = 0;
            }
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        if(n == 1) {
            printf("Case #%d: 1\n", ++cas);
            continue;
        }
        a[n] = 0;
        int pre = -1;
        for(int i = 1; i <= n; i++) {
            if(a[i] == 1) continue;
            dp[i] = 0;
            if(pre == -1) {
                if(i == 1) {
                    dp[i] = 1;
                    f[i][1] = 1;
                }
                else {
                    dp[i] = ibin[i - 1];
                    for(int j = 1; j <= i - 1; j++) {
                        if(j == 1) f[i][j] = 2 * ibin[i - 1] % mod;
                        else f[i][j] = ibin[i - j];
                    }
                }
            }
            else {
                int c = i - pre - 1;
                for(int j = n - 1; j >= 1; j--) {
                    if(j - c > 0) f[pre][j] = f[pre][j - c];
                    else f[pre][j] = 0;
                    g[pre][j] = (g[pre][j + 1] + 1LL * f[pre][j] * ibin[j] % mod) % mod;
                }
                for(int j = 1; j < n; j++) {
                    f[i][j] = 1LL * g[pre][j] * bin[j - 1] % mod;
                }

                for(int j = 1; j < n; j++) {
                    add(f[i][1], 1LL * f[pre][j] * ibin[j] % mod);
                    add(dp[i], 1LL * f[pre][j] * ibin[j] % mod);
                }
            }
            pre = i;
        }
        printf("Case #%d: %d\n", ++cas, dp[n]);
    }
    return 0;
}

/*
*/