• CF886E Maximum Element


    题目传送门CF886E

    洛谷入口

    题目大意

    (有个神仙写了个序列求max,它长下面这样:)

    int fast_max(int n,int a[]){
        int ans = 0;
        int offset = 0;
        for (int i = 0; i < n; ++i)
            if (ans < a[i]){
                ans = a[i];
                offset = 0;
            } else {
                offset = offset + 1;
                if(offset == k)
                    return ans;
            }
        return ans;
    }
    

    (现在他比较关心的是出错的情况,请你算出有多少个1~n的排列在这个函数的计算下答案不为n)
    (最终结果对10^9+7取模)

    数据范围

    (circ) (n,kle10^6)

    题解

    这题应该可以较为轻易地看出这要用DP
    题意要运算不正确序列的方案数
    那只要求出运算正确的方案数
    再用(n!)去减他就好了
    那先定状态为(f_i)为到第i位时运算正确的方案数
    那么会出来一个状态转移方程:
    (f_i=sumlimits_{j=i-k+1}^{i}A^{i-j}_{i-1} imes f_{j-1})
    意思就是对于f_i来说
    假设这i个数最大的i在第j位上
    那么在第j位后面距第i个有i-j个位置
    而有i-1个数可供选择填
    所以就有了(C^{i-j}_{i-1})这项的存在
    那个((i-j)!)意思就是把挑出来的这(i-j)个进行随便排
    f_{j-1}就是j之前的方案数这就不多说了
    当然为了满足只看k个的情况,需要注意j的枚举范围
    相信也可以理解了,最早只能在i-k+1,这样就能不重不漏地枚举了
    那么为了不超时成(O(nk)),下面开始化简(推式子)

    (高能部分开始↓↓)

    以下推式子可能会出现手误or脑子短路,望发现者在评论区纠正一下,谢谢!

    (f_i=sumlimits_{j=i-k+1}^{i}A^{i-j}_{i-1} imes f_{j-1})
    (=sumlimits_{j=i-k+1}^ifrac{(i-1)!}{[(i-1)-(i-j)]!} imes f_{j-1})
    (=sumlimits_{j=i-k+1}^ifrac{(i-1)!}{[(j-1)!} imes f_{j-1})
    (=sumlimits_{j=i-k+1}^i(i-1)! imesfrac{f_{j-1}}{(j-1)!})
    把j往前推一可得:
    (f_i=(i-1)! imessumlimits_{j=i-k}^{i-1}frac{f_j}{j!})
    所以这样就好多了
    那只要维护一下(frac{f_j}{j!})的前缀和就可以线性求出所有f_i的值了!
    最后最终答案就按照刚才的道理:
    (n!-sumlimits^n_{i=1}A^{n-i}_{n-1} imes f_{n-1})
    (=n!-(n-1)! imessumlimits^{n-1}_{i=0}frac{f_i}{i})
    这就是个前缀和后(O(1))可以解决的东西了
    那么预处理上需要所有(iin[1,n])的阶乘和阶乘的逆元
    用个递推就好,加个对(frac{f_i}{i})的前缀和,还有个快速幂
    这题就没问题了!
    (也看得出来这题的代码量很小)
    下面,上代码!↓↓↓

    AC代码

    #include<bits/stdc++.h>
    #define int  long long//粗暴的方法
    using namespace std;
    int f[1000010],g[1000010],sum[1000010],inv[1000010],n,k,th[1000010],mod=1e9+7;;
    inline int read(){
       int s=0,w=1;
       char ch=getchar();
       while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
       while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
       return s*w;
    }
    int ksm(int x,int y){
    	int tot=1,tmp=x;
    	while(y){
    		if(y&1)tot=tot*tmp%mod;
    		tmp=tmp*tmp%mod;
    		y>>=1;
    	}
    	return tot;
    }
    signed main(){
    	n=read();k=read();
    	th[0]=1;f[0]=1;sum[0]=1;
    	for(int i=1;i<=n;i++)th[i]=th[i-1]*i%mod;
    	inv[n]=ksm(th[n],mod-2);
    	for(int i=n-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
    	for(int i=1;i<=n;i++){
    		f[i]=th[i-1]*(sum[i-1]-sum[max(i-k,1LL*0)-1])%mod;
    		g[i]=f[i]*inv[i]%mod;
    		sum[i]=(sum[i-1]+g[i])%mod;
    	}
    	int ans=th[n-1]*sum[n-1]%mod;
    	cout<<(th[n]-ans+mod)%mod;
    } 
    

    支持一下吧,关注,点赞,评论都好!

    THE END

    Reality&Imagine
  • 相关阅读:
    4.使用webpack-dev-server工具实现自动打包编译的功能
    3.使用webpack配置文件webpack.confg.js配置打包文件的入口和出口
    2.webpack最基本的使用方式
    启动nginx 80端口被占用:tcp 0 0 127.0.0.1:80 127.0.0.1:34932 TIME_WAIT
    template标签就相当于React中的fragment
    初窥AST
    npm 中的 --
    你不知道的环境变量(转)
    非捕获分组 正向预查 与反向预查
    border-radius 如何计算
  • 原文地址:https://www.cnblogs.com/yang-RA-NOI/p/12597517.html
Copyright © 2020-2023  润新知