CF893E Counting Arrays 题解

题意

略。

分析

先考虑不为负数的情况,最后乘上 \(2^{y-1}\) 即可。

首先想到 dp,状态很好想,\(dp_{i,j}\) 表示长度为 \(i\) 的序列乘积为 \(j\) 的方案数,转移方程也就呼之欲出了:

\[dp_{i,j}=\sum\limits_{k|j} dp_{i-1,k} \]

但是很显然这样时间空间双爆,因此考虑优化:

由于 \(y \leq 10^6\),且 \(2^{19}<10^6<2^{20}\) 所以这个序列中不为 \(1\) 的项最多有 \(19\) 项,因此可以先算出来不带 \(1\) 的,最后再用排列组合算一下。现在的 \(dp_{i,j}\) 表示有 \(i\) 项不为 \(1\) 的序列乘积为 \(j\) 的方案数,这样预处理部分的时空问题就都解决了。

然后处理查询,枚举不含 \(1\) 的项数,对于不含 \(1\) 的项数 \(i\),需要将 \(y-i\)\(1\) 安排到 \(i+1\) 个空里(包括最前和最后),发现用小学奥数的插板法很容易做,设有 \(t\) 种安排方法,则有

\[t={i+1-1 \choose y-i+i+1-1}={i \choose y} \]

那么最后的答案就是

\[2^{y-1}\sum\limits_{i=0}^{\min(m,19)} t \cdot dp_{i,x} \]

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
#define getchar gc
inline void qread(){}template<class T1,class ...T2>
inline void qread(T1 &a,T2&... b)
{
    register T1 x=0;register bool f=false;char ch=getchar();
    while(ch<'0') f|=(ch=='-'),ch=getchar();
    while(ch>='0') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    x=(f?-x:x);a=x;qread(b...);
}
template<class T> T qmax(T x,T y){return x>y?x:y;}
template<class T,class ...Arg> T qmax(T x,T y,Arg ...arg){return qmax(x>y?x:y,arg...);}
template<class T> T qmin(T x,T y){return x<y?x:y;}
template<class T,class ...Arg> T qmin(T x,T y,Arg ...arg){return qmin(x<y?x:y,arg...);}
const int MAXN=1e6+7;
const int mod=1e9+7;
const int _=1e6;
int T,n,m,dp[21][MAXN],cnt,inv[_+7],fac[_+7],ifac[_+7],ans;
int fastpow(int base,int power,int modx)
{
    int res=1;
    while(power)
    {
        if(power&1) res=(res*base)%modx;
        power>>=1;base=(base*base)%modx;
    }return res%modx;
}
inline int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
signed main()
{
    qread(T);
    int i,j,k;
    inv[0]=inv[1]=fac[0]=ifac[0]=1;
    for(i=1;i<=_;i++) fac[i]=fac[i-1]*i%mod;
    for(i=2;i<=_;i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod;
    for(i=1;i<=_;i++) ifac[i]=ifac[i-1]*inv[i]%mod;dp[0][1]=1;
    for(i=0;i<=19;i++)
        for(j=1;j<=1e6;j++)
        {
            if(!dp[i][j]) continue;
            for(k=j+j;k<=1e6;k+=j) 
                dp[i+1][k]=((long long)dp[i+1][k]+(long long)dp[i][j])%(long long)mod;
        }
    while(T--)
    {
        qread(n,m);ans=0;
        for(i=0;i<=qmin(m,19ll);i++)
            if(dp[i][n]>0) ans=(ans+dp[i][n]*C(m,i))%mod;
        printf("%lld\n",(ans*fastpow(2,m-1,mod))%mod);
    }
    return 0;
}
posted @ 2022-06-15 18:00  l_x_y  阅读(85)  评论(0编辑  收藏  举报