• CF1174E Ehab and the Expected GCD Problem(DP,数论)


    题目大意:对于一个序列,定义它的价值是它的所有前缀的 $gcd$ 中互不相同的数的个数。给定整数 $n$,问在 $1$ 到 $n$ 的排列中,有多少个排列的价值达到最大值。答案对 $10^9+7$ 取模。

    $2le nle 10^6$。


    一道 Div. 2 的难度 2500 的题,真的不是吹的……

    首先考虑排列的第一个数 。假如分解质因子后为 $prod p_i^{c_i}$,那么此时排列价值的最大值为 $sum c_i$。

    为什么?因为如果 $gcd$ 变了,那么一定变成原来 $gcd$ 的约数。每次变化 $sum c_i$ 至少 $-1$。所以最大值就是 $sum c_i$。

    那么排列的价值达到最大值,只有在第一个数的 $sum c_i$ 达到最大值才可能,并且每次 $gcd$ 变化只会令 $sum c_i$ 减小 $1$。

    首先发现,质因子 $p_i$ 中不会有 $ge 5$ 的数。因为此时可以把 $p_i$ 变成 $2^2$,约数更多且仍然合法。

    然后,设分解质因子后 $3$ 的次数为 $c$,那么 $0le cle 1$。因为当 $cge 2$ 时,可以把 $3^2$ 变成 $2^3$,约数更多且仍然合法。

    所以第一个数可以被表示成 $2^x3^y$,其中 $yin{0,1}$。

    那么就能上DP了。(为什么每次都那么突然……)

    设 $f[i][x][y]$ 表示目前填了前 $i$ 位,当前的 $gcd$ 是 $2^x3^y$,的总合法序列数。

    初始状态 $f[1][lfloorlog_2n floor][0]=1$。如果 $2^{lfloorlog_2n floor-1} imes 3le n$,那么还有 $f[1][lfloorlog_2n floor-1][1]=1$。其它的状态无用,只有这两个状态的 $x+y$ 达到了最大值。

    答案为 $f[n][0][0]$。因为排列包含 $1$,所以 $gcd$ 一定会变为 $1$。

    如何转移?(以下设 $cnt(x)=lfloorfrac{n}{x} floor$,即 $x$ 的倍数的个数)

    • $gcd$ 不变。那么 $f[i][x][y]+=f[i-1][x][y](cnt(2^x3^y)-(i-1))$。因为新选择的数可以是且一定是 $2^x3^y$ 的倍数。然而前 $i-1$ 个位置都是 $2^x3^y$ 的倍数,所以要减掉。
    • $gcd/2$,也就是 $x--$(此时要求 $x<lfloorlog_2n floor$)。那么 $f[i][x][y]+=f[i-1][x+1][y](cnt(2^x3^y)-cnt(2^{x+1}3^y))$。因为新选择的数一定是 $2^x3^y$ 的倍数,但一定不是 $2^{x+1}3^y$ 的倍数(否则 $gcd$ 不变)。前 $i-1$ 个位置都是 $2^{x+1}3^y$ 的倍数,所以不用减掉。
    • $gcd/3$,也就是 $y--$(此时要求 $y=0$)。那么 $f[i][x][y]+=f[i-1][x][y+1](cnt(2^x3^y)-cnt(2^x3^{y+1}))$。

    时间复杂度 $O(nlog n)$。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int maxn=1000100,mod=1000000007;
    #define MP make_pair
    #define PB push_back
    #define lson o<<1,l,mid
    #define rson o<<1|1,mid+1,r
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline ll read(){
        char ch=getchar();ll x=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int n,lt,f[maxn][21][2];
    inline int cnt(int x){return n/x;}
    int main(){
        n=read();
        lt=log2(n);
        f[1][lt][0]=1;
        if((1<<(lt-1))*3<=n) f[1][lt-1][1]=1;
        FOR(i,2,n) FOR(j,0,lt){
            f[i][j][0]=(1ll*f[i-1][j][0]*(cnt(1<<j)-(i-1))+1ll*f[i-1][j+1][0]*(cnt(1<<j)-cnt(1<<(j+1)))+1ll*f[i-1][j][1]*(cnt(1<<j)-cnt((1<<j)*3)))%mod;
            f[i][j][1]=(1ll*f[i-1][j][1]*(cnt((1<<j)*3)-(i-1))+1ll*f[i-1][j+1][1]*(cnt((1<<j)*3)-cnt((1<<(j+1))*3)))%mod;
        }
        printf("%d
    ",f[n][0][0]);
    }
    View Code
  • 相关阅读:
    区分/不区分大小写的比较,查找字符串在另一字符串中的位置,字符串开头是否包括另一字符串 hasPrefix
    获取文件名以及后缀
    监听Documents文件夹内文件发生改变
    根据路径获取文件大小
    获取视频第一帧的方法
    判断图片格式
    iTunes文件共享
    iOS 10 隐私权限设置
    uCos 学习:0-有关概念
    ALSA 有关文档
  • 原文地址:https://www.cnblogs.com/1000Suns/p/10987188.html
Copyright © 2020-2023  润新知