Hello 2019 D 素因子贡献法计算期望 + 概率dp + 滚动数组

https://codeforces.com/contest/1097/problem/D

题意

给你一个n和k,问n经过k次操作之后留下的n的期望,每次操作n随机变成一个n的因数

题解

  • 概率dp计算出每个素因子留下的概率,乘以这个素因子的值就是这个素因子的贡献期望
  • 定义\(dp[i][j]\)为第i次操作后剩下j个素因子的概率,概率dp顺着推
    • \(dp[i][j]->dp[i+1][k](k<=j)\)
    • \(dp[i+1][k]+=dp[i][j]\frac{1}{j+1}(k<=j)\)
  • \(ans=\sum^{cnt}_{i=1}\sum^{num[i]}_{j=0}pr[i]^j*dp[k][j]\)
  • 二维滚动数组滚掉一维

代码

#include<bits/stdc++.h>
#define P 1000000007
#define M 32000000
#define MAXN 100
#define ll long long
using namespace std;
ll dp[2][MAXN],inv[MAXN],n,ans=1;
int vi[M],sz,k,pr[M];
void sieve(){
    inv[0]=inv[1]=1;
    for(int i=2;i<MAXN;i++)inv[i]=(P-P/i)*inv[P%i]%P;

    for(ll i=2;i<M;i++){
        if(!vi[i])pr[++sz]=i;
        for(int j=1;j<=sz&&pr[j]*i<M;j++){
            vi[pr[j]*i]=1;
            if(i%pr[j]==0)break;
        }
    }
}

int main(){
    sieve();
    cin>>n>>k;
    for(int i=1;i<=sz&&pr[i]<=n;i++){
        int cnt=0;
        if(n%pr[i])continue;
        while(n%pr[i]==0){
            n/=pr[i];
            cnt++;
        }
        memset(dp,0,sizeof(dp));          //
        dp[0][cnt]=1;
        int st=0;
        for(int j=0;j<k;j++){
            memset(dp[st^1],0,sizeof(dp[st^1]));
            for(int p=0;p<=cnt;p++){
                for(int q=0;q<=p;q++){
                    dp[st^1][q]+=dp[st][p]*inv[p+1]%P;
                    dp[st^1][q]%=P;
                }
            }
            st^=1;
        }
        ll mul=1,tp=0;
        for(int j=0;j<=cnt;j++){
            tp+=dp[st][j]*mul%P;
            tp%=P;
            mul=mul*pr[i]%P;
        }
        ans=ans*tp%P;                //
    }
    if(n>1){
        memset(dp,0,sizeof(dp));
        dp[0][1]=1;
        int st=0;
        for(int j=0;j<k;j++){
            memset(dp[st^1],0,sizeof(dp[st^1]));
            for(int p=0;p<=1;p++){
                for(int q=0;q<=p;q++){
                    dp[st^1][q]+=dp[st][p]*inv[p+1]%P;
                    dp[st^1][q]%=P;
                }
            }
            st^=1;
        }
        ll mul=1,tp=0;
        for(int j=0;j<=1;j++){
            tp+=dp[st][j]*mul%P;
            tp%=P;
            mul=mul*n%P;
        }
        ans=ans*tp%P;
    }
    cout<<ans;
}

原文地址:https://www.cnblogs.com/VIrtu0s0/p/10806133.html

时间: 2024-08-24 22:31:47

Hello 2019 D 素因子贡献法计算期望 + 概率dp + 滚动数组的相关文章

【BZOJ 3652】大新闻 数位dp+期望概率dp

并不难,只是和期望概率dp结合了一下.稍作推断就可以发现加密与不加密是两个互相独立的问题,这个时候我们分开算就好了.对于加密,我们按位统计和就好了;对于不加密,我们先假设所有数都找到了他能找到的最好的匹配(就是异或后为二进制最高位与n-1相等的最大数)并且算出其异或后的总和,然后我们按位贪心,带着所有的数(一开始我们假设所有的数是小于等于二进制最高位与n-1相等的最大数的所有数)从高位走向低位,每走一步,如果这一位是0,就会导致一半的数在这一位不能是1,减去这一半的数在这一位上的贡献,如果这一位

【NOIP模拟赛】黑红树 期望概率dp

这是一道比较水的期望概率dp但是考场想歪了.......我们可以发现奇数一定是不能掉下来的,因为若奇数掉下来那么上一次偶数一定不会好好待着,那么我们考虑,一个点掉下来一定是有h/2-1个红(黑),h/2+1个黑(红),而且一定是差不多相间的(我就是因为没有看出来这里才会去想组合数,然后......),那么我们发现只要一奇一偶,就可以组成一对,因为偶数一定是平的因此,我们发现在掉下来的那对之前都是红黑或黑红,但是到了这里就是红红或黑黑了,我们只要求出(异色的概率)^(h/2-1)*(同色的概率)就

HDU 3853 期望概率DP

期望概率DP简单题 从[1,1]点走到[r,c]点,每走一步的代价为2 给出每个点走相邻位置的概率,共3中方向,不动: [x,y]->[x][y]=p[x][y][0] ,  右移:[x][y]->[x][y+1]=p[x][y][1];  左移:[x][y]->[x+1][y]=p[x][y][2]; 问最后走到[r,c]的期望 dp[i][j]为从[i][j]点走到[r][c]的期望 有方程: dp[i][j]=    (dp[i][j]+2)*p[i][j][0]  +   (dp

POJ3682King Arthur&#39;s Birthday Celebration(数学期望||概率DP)

King Arthur is an narcissist who intends to spare no coins to celebrate his coming K-th birthday. The luxurious celebration will start on his birthday and King Arthur decides to let fate tell when to stop it. Every day he will toss a coin which has p

【BZOJ 2553】[BeiJing2011]禁忌 AC自动机+期望概率dp

我一开始想的是倒着来,发现太屎,后来想到了一种神奇的方法——我们带着一个既有期望又有概率的矩阵,偶数(2*id)代表期望,奇数(2*id+1)代表概率,初始答案矩阵一列,1的位置为1(起点为0),工具矩阵上如果是直接转移那么就是由i到j概率期望都乘上1/alphabet,特别的,对于一个包含禁忌串的节点直接由其父节点指向0,而且在计算期望是多加上他的概率,最后统计答案时把答案矩阵上所有的期望加和即可,这个方法很完美的被卡精了....... #include <cstdio> #include

蓝桥杯 - 概率计算 (概率DP)

题目传送:蓝桥杯 - 概率计算 概率计算 时间限制:1.0s   内存限制:256.0MB 锦囊1 锦囊2 锦囊3 问题描述 生成n个∈[a,b]的随机整数,输出它们的和为x的概率. 输入格式 一行输入四个整数依次为n,a,b,x,用空格分隔. 输出格式 输出一行包含一个小数位和为x的概率,小数点后保留四位小数 样例输入 2 1 3 4 样例输出 0.3333 数据规模和约定 对于50%的数据,n≤5. 对于100%的数据,n≤100,b≤100. 思路:概率DP,好久没做DP题了,居然1A了,

【BZOJ 4832】 [Lydsy2017年4月月赛] 抵制克苏恩 期望概率dp

打记录的题打多了,忘了用开维记录信息了......我们用f[i][j][l][k]表示已经完成了i次攻击,随从3血剩j个,2血剩l个,1血剩k个,这样我们求出每个状态的概率,从而求出他们对答案的贡献并加和,一开始我用的期望忘了转移的时候不能用1而要用概率...... #include <cstdio> #include <cstring> #define r register using namespace std; typedef long double LD; inline i

BZOJ 4318: OSU! 期望概率dp

这道15行的水题我竟然做了两节课...... 若是f[i][0]=(1-p)*f[i][0]+(1-p)*f[i][1],f[i][1]=p*(f[i-1][0]+1.0)+p*(f[i-1][1]+OOXX); 我们合并一下f[i]=p*1.0+p*OOXX=p*OX; OX:就是期望x^3的差,也就是(x+1)^3=x^3+3*x^2+3*x+1.0,中的3*x^2+3*x+1.0,这样我们要维护x^2以及x注意这里的x^2和x是指结尾的长度x #include<cstdio> doubl

[HEOI2017]分手是祝愿 期望概率dp 差分

经分析可知:I.操作每个灯可看做一种异或状态 II.每个状态可看做是一些异或状态的异或和,而且每个异或状态只能由它本身释放或放入 III.每一种异或状态只有存在不存在两中可行状态,因此这些灯只有同时处于不存在才可以,而两种异或状态之间没有关系因此可以把这些状态看做一样的,因此counts的是异或状态数. 到这里为止我们可以得到一个简单的转移方程 f[i]=i/n*f[i-1]+(n-i)/i*f[i+1]+1 于是看起来似乎已经到了解决问题的时候,所以我就开始推.......然后就没有然后了,由