E. Counting Arrays

题意:给定一个长度为n,要求乘积为m,其中组成m的数要求是整数

思路:首先有个很显然的想法:设f[i][j]表示前i个点乘积为j的最小值。因为询问数很多,所以必须离线把所有的东西都处理出来。

转移:f[i][j]=\sum f[i-1][j/k],考虑道空间会爆,所以可以把1先不考虑。

那么相乘次数就不会超过20次,是log级别的。

对于负数而言,手玩一下发现是2^{n-1}的。

对于1而言,设每个非1的位置为k,那么就有k+1个空间给1插入。

所以考虑插板法,如果有N个数插入M个空间:

1.每个空间至少插1个数:C(N-1,M-1)

2.每个空间最少插0个数:C(N+M-1,M-1)

其中:N=n-iM=i+1

所以贡献是f[i][m]*C(n,i)

复杂度O(nlog^2n)

#include <bits/stdc++.h>

#define int long long
using namespace std;

const int N = 1e6 + 10 , M = 21 , mod = 1e9 + 7;

int f[M][N],fac[N],inv[N];
int q,n,maxn=1e6,m;
bool vis[N];
int fpow(int a,int b){
	int res=1;
	while(b){
		if(b&1)  res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}
int C(int x,int y){
	if(x==y)  return 1;
	if(x>y||!x||!y)  return 0;
	return fac[y]*inv[x]%mod*inv[y-x]%mod;
}
int invv(int x){
	return fpow(x,mod-2);
}
void solve(){
	fac[0]=inv[0]=1;
	for(int i=1;i<=maxn;i++){
		fac[i]=fac[i-1]*i%mod; 
		inv[i]=invv(fac[i]);
	}   
	for(int i=1;i<=maxn;i++)  f[1][i]=1;
	for(int i=1;i<=20;i++){
		for(int j=2;j<=maxn;++j){
			if(!f[i][j])  continue;
    		for(int k=2;k*j<=maxn;k++){
    			f[i+1][k*j]=(f[i+1][k*j]+f[i][j])%mod;
			}
		}
	}
}
signed main(){
	solve();
	cin>>q;
	while(q--){
		cin>>m>>n;
		if(m==1){
			cout<<fpow(2,n-1)<<endl;
			continue;
		}
		int res=0;
		for(int i=1;i<=min(n,20ll);i++){
            res=(res+f[i][m]*C(i,n))%mod;
            //cout<<f[i][m]<<" ";
		}
		res=(res*fpow(2,n-1)%mod)%mod;
		cout<<res<<endl;
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值