HDU 6327 Random Sequence(动态规划)

题目链接:Random Sequence

题意

有一个长度为 n n 的序列 a1,a2,,an (0aim) 和长度为 m m 的序列 v1,v2,,vm,如果 ai=0 a i = 0 ,则 ai a i 的值可能为 1 1 m 之间的任意值,取得任意值的概率为 1m 1 m ,求公式:

i=1n3vgcd(ai,ai+1,ai+2,ai+3) ∏ i = 1 n − 3 v gcd ( a i , a i + 1 , a i + 2 , a i + 3 )
的期望值。

输入

第一行为一个整数 T (1T10) T   ( 1 ≤ T ≤ 10 ) ,接下去有 T T 组数据,每组数据第一行为两个整数 n,m (4n100,1m100),第二行为 n n 个整数 a1,a2,,an (0aim),第三行为 m m 个整数 v1,v2,,vm (1vi109)

输出

输出所求公式的期望值对 109+7 10 9 + 7 取模后的结果,若期望值无法用整数表示,则先将期望值化简为 AB A B 后,输出 C (0C<109+7) C   ( 0 ≤ C < 10 9 + 7 ) 的值,其中 C C 满足 A=B×C(mod109+7)

样例

输入
2
6 8
4 8 8 4 6 5
10 20 30 40 50 60 70 80
4 3
0 0 0 0
3 2 4
输出
8000
3
题解

由于任意两个 v v 之间的选择是相互独立的,所以 E(v1v2)=E(v1)E(v2)
如果定义状态 dp[i][x][y][z] d p [ i ] [ x ] [ y ] [ z ] 表示到第 i i ai=x,ai1=y,ai2=z ij=4vgcd(aj,aj1,aj2,aj3) ∏ j = 4 i v gcd ( a j , a j − 1 , a j − 2 , a j − 3 ) 的所有合法情况的和,则可以通过枚举第 ai+1 a i + 1 位的所有合法值 xx x x 计算 dp[i+1][xx][x][y] d p [ i + 1 ] [ x x ] [ x ] [ y ] 的值,其递推公式为:

dp[i+1][xx][x][y]=x,y,z[1,m]dp[i][x][y][z]×vgcd(xx,x,y,z) d p [ i + 1 ] [ x x ] [ x ] [ y ] = ∑ x , y , z ∈ [ 1 , m ] d p [ i ] [ x ] [ y ] [ z ] × v gcd ( x x , x , y , z )
注意当 ai a i 的值非 0 0 x 的值只能取 ai a i ,只有 ai a i 的值为 0 0 x 才可以从 1 1 m 枚举所有值,对 xx,y,z x x , y , z 同理。这种递推式的时间复杂度为 O(nm4) O ( n m 4 ) ,必然超时。
我们可以定义状态 dp[i][x][y][z] d p [ i ] [ x ] [ y ] [ z ] 表示到第 i i ai=x,gcd(x,ai1)=y,gcd(x,ai1,ai2)=z 时所有合法情况下 ij=4vgcd(aj,aj1,aj2,aj3) ∏ j = 4 i v gcd ( a j , a j − 1 , a j − 2 , a j − 3 ) 的和,由公约数关系可以得到, x,y,z x , y , z 必须满足 z z 能整除 y y y 整除 x,于是有下列递推式:
dp[i+1][xx][gcd(xx,x)][gcd(xx,y)]=x,y,z[1,m],z|y,y|xdp[i][x][y][z]×vgcd(xx,z) d p [ i + 1 ] [ x x ] [ gcd ( x x , x ) ] [ gcd ( x x , y ) ] = ∑ x , y , z ∈ [ 1 , m ] , z | y , y | x d p [ i ] [ x ] [ y ] [ z ] × v gcd ( x x , z )
其中 xx,x,y,z x x , x , y , z ai a i 0 0 时的取值限制同上,a|b 表示 a a 能整除 b,在 z|y,y|x z | y , y | x 的限制下, x,y,z x , y , z 所有合法的状态可以减少到 1471 1471 种,因此时间复杂度为 O(1471×nm) O ( 1471 × n m ) ,其中 gcd gcd 的计算可以 O(m2logm) O ( m 2 log ⁡ m ) 通过预处理将复杂度降低到 O(1) O ( 1 )
最后是 dp d p 的初始值的确定:
dp[3][x][gcd(x,y)][gcd(x,y,z)]=x,y,z[1,m]1 d p [ 3 ] [ x ] [ gcd ( x , y ) ] [ gcd ( x , y , z ) ] = ∑ x , y , z ∈ [ 1 , m ] 1
其中 x,y,z x , y , z 的取值限制同上, dp d p 初始值即表示取前 3 3 位为 x,y,z 的合法状态数,系数为 1 1 ,初始化方式为暴力,时间复杂度为 O(m3)
最后将 x,y,z[1,m],z|y,y|xdp[n][x][y][z] ∑ x , y , z ∈ [ 1 , m ] , z | y , y | x d p [ n ] [ x ] [ y ] [ z ] 乘上总方案数对 109+7 10 9 + 7 的逆元就是答案,总方案数为 mcnt m c n t ,其中 cnt c n t a a 序列中 0 的个数。

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;

#define LL long long
const int maxn = 101;
const int MOD = 1000000007;
int T, n, m, ans, cnt, inv;
int a[maxn], v[maxn];
int Gcd[maxn][maxn], dp[maxn][maxn][maxn][maxn];

int add(int a, int b) {
    a += b;
    if(a >= MOD) {
        return a - MOD;
    }
    if(a < 0) {
        return a + MOD;
    }
    return a;
}

int gcd(int x, int y) {
    return y == 0? x: gcd(y, x % y);
}

void prepare_gcd() {
    for(int i = 1; i < maxn; ++i) {
        Gcd[0][i] = Gcd[i][0] = i;
        for(int j = 1; j < maxn; ++j) {
            Gcd[i][j] = gcd(i, j);
        }
    }
}

void Init() {
    for(int i = 3; i <= n; ++i) {
        for(int z = 1; z <= m; ++z) {
            for(int y = z; y <= m; y += z) {
                for(int x = y; x <= m; x += y) {
                    dp[i][x][y][z] = 0;
                }
            }
        }
    }
    for(int x = 1; x <= m; ++x) {
        if(a[3] != 0 && x != a[3]) {
            continue;
        }
        for(int y = 1; y <= m; ++y) {
            if(a[2] != 0 && a[2] != y) {
                continue;
            }
            for(int z = 1; z <= m; ++z) {
                if(a[1] != 0 && z != a[1]) {
                    continue;
                }
                ++dp[3][x][Gcd[x][y]][Gcd[Gcd[x][y]][z]];
            }
        }
    }
}

LL fast_pow(LL res, LL n) {
    LL ans;
    for(ans = 1; n != 0; n >>= 1) {
        if((n & 1) == 1) {
            ans = (ans * res) % MOD;
        }
        res = (res * res) % MOD;
    }
    return ans;
}

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
//    freopen("test1.out", "w", stdout);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    prepare_gcd();
    scanf("%d", &T);
    while(T--) {
        cnt = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            if(a[i] == 0) {
                ++cnt;
            }
        }
        for(int i = 1; i <= m; ++i) {
            scanf("%d", &v[i]);
        }
        Init();
        for(int i = 3; i < n; ++i) {
            for(int z = 1; z <= m; ++z) {
                for(int y = z; y <= m; y += z) {
                    for(int x = y; x <= m; x += y) {
                        if(dp[i][x][y][z] == 0) {
                            continue;
                        }
                        for(int xx = 1; xx <= m; ++xx) {
                            if(a[i + 1] != 0 && a[i + 1] != xx) {
                                continue;
                            }
                            dp[i + 1][xx][Gcd[xx][x]][Gcd[xx][y]] = add(dp[i + 1][xx][Gcd[xx][x]][Gcd[xx][y]], (LL)dp[i][x][y][z] * v[Gcd[xx][z]] % MOD);
                        }
                    }
                }
            }
        }
        ans = 0;
        for(int k = 1; k <= m; ++k) {
            for(int j = k; j <= m; j += k) {
                for(int i = j; i <= m; i += j) {
                    ans = add(ans, dp[n][i][j][k]);
                }
            }
        }
        ans = (LL)ans * fast_pow(fast_pow(m, cnt), MOD - 2) % MOD;
        printf("%d\n", ans);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值