zoukankan      html  css  js  c++  java
  • 【xsy2303】呀 dp

    题目大意:你需要构造一个长度为$n$的排列$A$,使得里面包含有子序列$B$(子序列$B$为一个给定的$1$到$m$的排列),且对于每个$i$,有$A[A[i]]=i$,问有多少种方案方案。

    数据范围:$n≤10^7$,$m≤500$,答案对$10^9+7$取模

    我们首先不考虑有m的存在,考虑如何构造一个符合条件的序列$A$。

    我们发现我们可以DP,设$f[i]$表示有多少种长度为i的序列满足$A[A[i]]=i$。

    对于第$i$个数,我们可以考虑把它填在原位,或者放在第j个位置,然后在$A[i]$处填上$j$。

    根据这个,不难推出$f[i]=(i-1)f[i-2]+f[i-1]$。

    我们下面考虑,序列$B$的前$k$个数,位于$A$中前$m$个位置,剩余的$m-k$个,位于后$n-m$个位置。

    我们发现,对于剩余的$m-k$个数,对应的$A_B[i]$,都会被占用,用来填(填写了$B[i]$的位置)。

    所以我们需要在前$m$个位置里,用恰好$k$个位置,按顺序填出序列$B$前$k$个数字。

    不难发现,至多只有一种填法。

    对于$B[i]$,我们找到序列$A$中第i个可以填数的地方,直接填入$B[i]$即可。

    最后$O(m)$扫一遍判断即可。

    如果可行,那么剩下的$n-k$个位置中,需要找出$m-k$个位置放置$B[k+1],B[k+2].....B[m]$,并且在$A[B[k+1]]$等地方填上它们的位置。

    剩下的$n-2m+k$个位置,就可以随便填>m的数了,方案数显然是$f[n-2m+k]$

    所以方案数为$inom{n-k}{m-k} imes f[n-2m+k]$。

    我们对于每个不超过m的k全部处理一遍就可以了。

    时间复杂度:$O(n+m^2)$

     1 #include<bits/stdc++.h>
     2 #define MOD 1000000007
     3 #define L long long
     4 #define M 10000005
     5 using namespace std;
     6 
     7 L fac[M]={0},invfac[M]={0},f[M]={0};
     8 L pow_mod(L x,L k){L ans=1;for(;k;k>>=1,x=x*x%MOD) if(k&1) ans=ans*x%MOD; return ans;}
     9 L C(int n,int m){return fac[n]*invfac[m]%MOD*invfac[n-m]%MOD;}
    10 
    11 L mark[M]={0},b[M]={0},a[M]={0},ans=0,n,m;
    12 void solve(int k){
    13     if(n-m<m-k) return;
    14     for(int i=1,j=1;i<=k;i++,j++){
    15         while(!mark[j]) j++;
    16         a[j]=b[i];
    17     }
    18     for(int i=1;i<=m;i++)
    19     if(mark[i]&&a[a[i]]!=i) return;
    20     ans=(ans+C(n-m,m-k)*f[n-m-m+k])%MOD;
    21 }
    22 int main(){
    23     fac[0]=1; for(int i=1;i<M;i++) fac[i]=fac[i-1]*i%MOD;
    24     invfac[M-1]=pow_mod(fac[M-1],MOD-2);
    25     for(int i=M-2;~i;i--) invfac[i]=invfac[i+1]*(i+1)%MOD;
    26     f[0]=f[1]=1; for(int i=2;i<M;i++) f[i]=(f[i-1]+f[i-2]*(i-1))%MOD;
    27     
    28     scanf("%d%d",&n,&m);
    29     for(int i=1;i<=m;i++) scanf("%d",b+i);
    30     for(int k=0;k<=m;k++){
    31         mark[b[k]]=1;
    32         solve(k);
    33     }
    34     cout<<ans<<endl;
    35 }
  • 相关阅读:
    VS2008 SP1 安装时异常处理
    Android和iOS自带的人脸检测API
    FAAST 0.08 动作列表
    C#日期格式化(转)
    希望看这篇文章的人,耐心的看完这个短片
    C#实现转换十六进制
    App Store生存法则:iOS开发者经验分享
    开始的关键不是什么时候开始 而是开始后的坚持
    Eclipse IDE
    最近需要看的网站
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10777575.html
Copyright © 2011-2022 走看看