• 【SDOI2016】排列计数


    原题:

     n,m<=1e6,多组数据测试,数据组数<=5e5

    因为a是排列,所以若让ai=i那就让它别动

    选m个别动,剩下的重排列,要求每个人都不能在自己的位置上,求方案数

    这不就错位排序么

    关于错位排序以前写了个详解,现在发现若序列递推也听简单的

    令f(n)为长度为n的序列错位排序的方案数

    欲从f(n-1)递推到f(n),考虑长度n-1时的某一个方案

    方法一:直接从n-1个数里选一个,跟第n个数交换,易证这样是对的

    方法二:如果n-1个数里恰好有一个数在自己的位置上,那么交换这个数和第n个数也能得到合法方案

    如果n-1个数里有一个以上的数不在自己位置上,易证此时得不到合法方案

    所以方法一+方法二能不重复不遗漏地得到n个数的方案数

    那么容易得到f(n)=(n-1)*f(n-1)+(n-1)*f(n-2)=(n-1)*[f(n-1)+f(n-2)]

    那么本题答案就是f(n-m)*c(n,m)

    现在的问题是求组合数,因为数据组数很大,所以不能暴力求组合数

    而n和m也很大,不能递推求组合数

    怎么破?

    有同学说了,卢卡斯

    卢卡斯大概是可以的,但是还有更简单的办法

    那就是直接预处理出1e5以内的阶乘,就能公式法求组合数了

    注意:

    0个数的错位排序方案数为1

    表现在本题中,就是当n=m的时候,恰好有f(0)*c(n,m)=1个方案

    如果不特判这个就一分都拿不到

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 #define LL long long
     5 const int mo=1000000007;
     6 int n,m;
     7 LL f[1100000];
     8 LL j[1100000];
     9 LL qcp(LL x,LL y){
    10     LL bwl=1,z=x;
    11     for(;y;y>>=1){
    12         if(y&1)  bwl=bwl*z%mo;
    13         z=z*z%mo;
    14     }
    15     return bwl;
    16 }
    17 int main(){
    18     f[0]=1,f[1]=0,f[2]=1;
    19     //注意f[0]
    20     for(int i=3;i<=1000000;++i)  f[i]=(i-1)*(f[i-1]+f[i-2])%mo;
    21     j[0]=1;
    22     for(int i=1;i<=1000000;++i)  j[i]=j[i-1]*i%mo;
    23     int T;  cin>>T;
    24     while(T --> 0){
    25         scanf("%d%d",&n,&m);
    26         printf("%lld
    ",f[n-m]*j[n]%mo*qcp(j[m],mo-2)%mo*qcp(j[n-m],mo-2)%mo);
    27     }
    28     return 0;
    29 }
    View Code
  • 相关阅读:
    二分查找法
    AES算法工具类
    SHA加密算法工具类
    使用SQL创建唯一索引
    springboot 启动类CommandLineRunner(转载)
    MD5加密算法工具类
    Android 通过Socket 和服务器通讯
    android 网络连接判断
    android 文件上传,中文utf-8编码
    github打不开问题
  • 原文地址:https://www.cnblogs.com/cdcq/p/12658008.html
Copyright © 2020-2023  润新知