Codeforces 1174 E Ehab and the Expected GCD Problem —— DP,排列

202 篇文章 5 订阅

This way

题意:

现在有长度为n的排列,定义gi表示前i个数的gcd。f§表示排列p的g的不同的个数。定义fmax表示所有排列的f§的最大值。问你有多少f§=fmax

题解:

这道题把我搞晕了,,
首先可以想到的是一定是 2 k 2^k 2k是在第一位。然后就是2的幂次的阶梯式下降。
因为这个在唯一分解之后一定是质因子最多的一个数。那么考虑其它质数是否可能,发现在 2 k − 1 ∗ 3 < = n 2^{k-1}*3<=n 2k13<=n时会出现唯一一个3,拥有再多或者再大的质数的话一定没有2的组合来的优。
那么dp[i][j][k]就表示到第i个位置,2的幂次有j个,3的幂次有k个的情况数。
首先考虑下一个位置幂次不减少,那么

dp[i][j][0]=(dp[i][j][0]+mul(n/t[j]-i+1,dp[i-1][j][0]))%mod;

,因为之前i-1个数一定是2的j次的倍数,所以减掉即可。有一个3的转移类似。
接下来是减少了一个2的情况:

dp[i][j][0]=(dp[i][j][0]+mul(n/t[j]-n/t[j+1],dp[i-1][j+1][0]))%mod;

因为是少了一个二,所以需要减掉所有2的j+1次的请款。前面i-1个数一定是 2 j + 1 2^{j+1} 2j+1的倍数,所以不需要考虑。有一个3的情况类似。
最后是少一个3.

dp[i][j][0]=(dp[i][j][0]+mul(dp[i-1][j][1],n/t[j]-n/t[j]/3))%mod;

它一定是 2 j 2^j 2j的倍数减去 2 j ∗ 3 2^j*3 2j3的倍数,因为前面的数一定是 2 j ∗ 3 2^j*3 2j3的倍数,所以不需要考虑。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+5;
const ll mod=1e9+7;
int dp[N][25][2],t[25];
ll mul(int x,int y){
    return (ll)x*y%mod;
}
int main()
{
    ll n;
    scanf("%lld",&n);
    int ret=1,cnt=0;
    t[cnt]=ret;
    while(ret<=n)
        ret*=2,cnt++,t[cnt]=ret;
    ret/=2,cnt--;
    dp[1][cnt][0]=1;
    if(ret/2*3<=n)
        dp[1][cnt-1][1]=1;
    for(int i=2;i<=n;i++){
        for(int j=0;j<=cnt;j++){
            if(n/t[j]>i-1)
                dp[i][j][0]=(dp[i][j][0]+mul(n/t[j]-i+1,dp[i-1][j][0]))%mod;
            dp[i][j][0]=(dp[i][j][0]+mul(n/t[j]-n/t[j+1],dp[i-1][j+1][0]))%mod;
            dp[i][j][1]=(dp[i][j][1]+mul(n/t[j]/3-n/t[j+1]/3,dp[i-1][j+1][1]))%mod;
            if(n/t[j]/3>i-1)
                dp[i][j][1]=(dp[i][j][1]+mul(n/t[j]/3-i+1,dp[i-1][j][1]))%mod;
            dp[i][j][0]=(dp[i][j][0]+mul(dp[i-1][j][1],n/t[j]-n/t[j]/3))%mod;
        }
    }
    printf("%d\n",dp[n][0][0]);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值