hdu 6327 Random Sequence

Problem Description
There is a positive integer sequence a1,a2,…,an with some unknown positions, denoted by 0. Little Q will replace each 0 by a random integer within the range [1,m] equiprobably. After that, he will calculate the value of this sequence using the following formula :
∏i=1n−3v[gcd(ai,ai+1,ai+2,ai+3)]

Little Q is wondering what is the expected value of this sequence. Please write a program to calculate the expected value.

Input
The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
In each test case, there are 2 integers n,m(4≤n≤100,1≤m≤100) in the first line, denoting the length of the sequence and the bound of each number.
In the second line, there are n integers a1,a2,…,an(0≤ai≤m), denoting the sequence.
In the third line, there are m integers v1,v2,…vm(1≤vi≤109), denoting the array v.

Output
For each test case, print a single line containing an integer, denoting the expected value. If the answer is AB, please print C(0≤C<109+7) where A≡C×B(mod109+7).

Sample Input
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

Sample Output
8000
3

题意:求期望,
做法:我最开始的思路是每相邻的4个算一下,然后求期望,后来发现错了,因为这题不同位置之间是有相互影响的。然后就错了,,
正确做法:由于对期望不怎么理解,所以只能用期望等于概率乘以权值来推理状态转移,dp[i][j][k][l] 代表第i位取值为j,gcd(a[i],a[i-1]) =j,gcd(a[i],a[i-1],a[i-2]) = k时的期望,
初始化:首先暴力枚举前三个位置,可以得到每种情况的方案数,然后每个状态的初始化期望就是这个状态的方案数个数除以总方案数,可以用逆元来保存这个值,
状态转移:对于第i位,如果它是固定数,那么因为e = sum(p*v) ;对于每一个可以转移过来的状态,可知p没有发生变化,但是v变成了当前的val * v;由
e = sum(p*v) = sum(p * val *v) ;所以直接把前一个状态的期望乘以当前的数值,存到这个状态就好了
如果第i为是0,那么它可以有1-m种选择,所有前一个状态转移到当前状态的时候概率会变成p/m,由e = sum(p *v)可知,e = sum(p*v*val/m);
最后对所有状态的期望求和就行了。

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int num[N];
int v[N];
int dp[2][N][N][N];
const int mod = 1e9+7;
vector<int> vp[105];
int n,m;
int cnt[5];
int res[105];
int st[105][105];
int tmp;
void add(int &x,int y){
    x += y;
    if(x >= mod) x -= mod;
}
void init(){
    for(int i = 1;i <= 100;i ++){
        for(int j = 1;j <= 100;j++){
            st[i][j] = __gcd(i,j);
        }
    }
    for(int i = 1;i <= 100;i ++){
        for(int j = 1;j <= i;j ++){
            if(i%j == 0)vp[i].push_back(j);
        }
    }
}
void dfs(int x,int mm){
    if(x == 1){
        if(num[x])
            add(dp[tmp][cnt[3]][cnt[2]][st[cnt[2]][num[1]]],mm);
        else {
            for(int i = 1;i <= m;i ++) {
                add(dp[tmp][cnt[3]][cnt[2]][st[cnt[2]][i]],mm);
            }
        }
        return ;
    }
    if(num[x]==0){
        for(int i = 1;i <= m;i ++) {
            if(x ==3) cnt[x] = i;
            else cnt[x] = st[cnt[x+1]][i];
            dfs(x-1,mm);
        }
    }
    else{
        if(x ==3) cnt[x] = num[x];
        else cnt[x] = st[cnt[x+1]][num[x]];
        dfs(x-1,mm);
    }
}

int qpow(int x,int y){
    long long sum = 1;
    long long now = x;
    while(y){
        if(y&1) sum=  sum*now%mod;
        now =now*now%mod;
        y >>= 1;
    }
    return sum;
}
void cle(int x){
    for(int i = 1;i<= m;i ++){
        for(int j = 0;j < vp[i].size();j ++){
            int js = vp[i][j];
            for(int k = 0;k < vp[js].size();k ++){
                int ks = vp[js][k];
                dp[tmp][i][js][ks] = x;
            }
        }
    }
}
int get(){
    int ret = 0;
    for(int i =1;i <= m;i ++){
        for(int j = 0;j < vp[i].size();j ++){
            int js = vp[i][j];
            for(int k = 0;k < vp[js].size();k ++){
                int ks = vp[js][k];
                add(ret,dp[tmp][i][js][ks]);
            }
        }
    }
    return ret;
}
int main(){
    int T;
    init();
    cin >> T;
    while(T--){
        memset(dp,0,sizeof(dp));
        scanf("%d %d",&n,&m);
        long long vm = qpow(m,mod-2);
        for(int i = 1;i <= n;i ++) scanf("%d",&num[i]);
        for(int i = 1;i <= m;i ++) scanf("%d",&v[i]);
        tmp = 0;
        int inv = 1;
        for(int i = 1;i <= 3;i ++) if(num[i] == 0) inv = inv*m;
        inv = qpow(inv,mod-2);
        cle(0);
        dfs(3,inv);

        long long fa = 1,fb = 1;
        for(int i = 4;i <= n;i ++){
            memset(res,0,sizeof(res));
            tmp = 1-tmp;
            cle(0);
            if(num[i]){
                for(int j = 1;j <= m;j ++){
                    for(int k = 0;k < vp[j].size();k ++){
                        int ks = vp[j][k];
                        for(int l = 0;l < vp[ks].size();l ++){
                            int ls = vp[ks][l];
                            int ps = st[num[i]][ls];
                            add(dp[tmp][num[i]][st[num[i]][j]][st[num[i]][ks]],1LL*v[ps]*dp[1-tmp][j][ks][ls]%mod);
                        }
                    }
                }
            }
            else{
                for(int t = 1;t <= m;t ++){
                    for(int j = 1;j <= m;j ++){
                        for(int k = 0;k < vp[j].size();k ++){
                            int ks = vp[j][k];
                            for(int l = 0;l < vp[ks].size();l ++){
                                int ls = vp[ks][l];
                                int ps = st[t][ls];
                                add(dp[tmp][t][st[t][j]][st[t][ks]],1LL*vm*v[ps]%mod*dp[1-tmp][j][ks][ls]%mod);
                            }
                        }
                    }
                }
            }
        }
        printf("%d\n",get());
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值