• CF886E Maximum Element


    CF886E Maximum Element

    原题链接

    洛谷链接

    本文公式引自mjy的课件

    这里涉及到一个很有意思的trick:对于只关心相对大小的题目,我们可以只考虑相对大小。

    我们先定义(f_i)为1~i的排列中有多少个是完成循环之后没有退出的(即合法序列)

    那么我们可以考虑转移:(f_{j-1} imesleft(egin{array}{c}{i-1} \ {i-j}end{array} ight) imes(i-j) !)

    这里的含义是枚举最大值(j)(jin [i-k+1,i])),那么对于最大值前面的元素一共有(f_{j-1})种排法。这种做法的精髓在于,将([1,n])中任意(j-1)个数分配给前(j-1)个位置,排列方法都只有(f_{j-1})种。(因为我们只要求相对大小,想想就知道为什么)

    理解了前一部分,后面两项就很好理解了。

    我们可以化一下这个式子:

    [=f_{j-1} imes(i-1) ! imes frac{1}{(j-1) !} ]

    再整理一下:

    [egin{array}{l}{f_{i}=sum_{j=i-k+1}^{i} frac{f_{j-1}}{(j-1) !} imes(i-1) !} \ {=(i-1) ! sum_{j=i-k}^{i-1} frac{f_{j}}{j !}}end{array} ]

    很显然,我们对((i-1) !)(frac{f_{j}}{j !})分别维护一个前缀和就好啦~

    最后,因为题目中规定,即使中途退出也可能是正确的。所以我们再用相似的方法做一下就好了:

    [a n s=n !-sum_{i=1}^{n} f_{i-1} imesleft(egin{array}{c}{n-1} \ {n-i}end{array} ight) imes(n-i) ! ]

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define maxn (int)(1e6+100)
    #define mod (int)(1e9+7)
    #define ll long long
    using namespace std;
    int n,k;
    ll inv[maxn],fact[maxn],pre[maxn],dp[maxn];
    ll ksm(ll x,ll times) {
        ll cur=x,ans=1;
        while(times) {
            if(times&1)ans*=cur,ans%=mod;
            cur*=cur;cur%=mod;times>>=1;
        }
        return ans;
    }
    ll c(ll tot,ll chose) {
        return (fact[tot]*(ksm((fact[chose]*fact[tot-chose])%mod,mod-2)%mod))%mod;
    }
    
    int main() {
        scanf("%d%d",&n,&k);
        pre[0]=fact[1]=fact[0]=1;
        for(int i=2;i<maxn;i++){fact[i]=fact[i-1]*i;fact[i]%=mod;}
        for(int i=1;i<maxn;i++){inv[i]=ksm(i,mod-2);}
        //cout<<">>"<<(inv[3]*9)%mod<<endl;
        dp[1]=1;pre[1]=1;dp[0]=1;
        for(int i=1;i<=k;i++)dp[i]=fact[i],pre[i]=pre[i-1]+dp[i]*ksm(fact[i],mod-2),pre[i]%=mod;
        for(int i=k+1;i<=n;i++) {
            dp[i]=(fact[i-1]*(pre[i-1]-pre[i-k-1]+mod)%mod)%mod;
            pre[i]=pre[i-1]+dp[i]*ksm(fact[i],mod-2) % mod;pre[i]%=mod;
        }
        ll ans=0;//=fact[n]-;
        for(int i=1;i<=n;i++) {
            ans-=(((dp[i-1]*c(n-1,n-i))%mod)*fact[n-i])%mod,ans += mod;ans%=mod;
        }
        ans+=fact[n];ans%=mod;ans+=mod;ans%=mod;
        printf("%lld",ans);
        return 0;
    }
    

  • 相关阅读:
    作为一枚第二天上班的小小.net程序员(技术宅的那种)很迷茫哦,第一个随笔
    清除NT Kernel & System占用80端口
    case then 的用法 貌似case then不支持别名
    syscomments 可以用来查找所有关于库中用到的某个关键词的所有相关脚本
    查看系统表存储过程名称SELECT *,OBJECT_NAME(id) FROM syscomments
    毫秒级百万数据分页存储过程
    使用sp_configure启用 'Ad Hoc Distributed Queries'
    使用 ServKit(PHPnow) 搭建 PHP 环境[图]
    apache+php+mysql常见集成环境安装包
    Quartz.NET作业调度框架详解
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/11297141.html
Copyright © 2020-2023  润新知