HDU7217 Counting Good Arrays 题解
题目描述
\(T\) 组数据,求满足以下条件的正整数序列 \(a\) 个数,对 \(10^9+7\) 取模:
- 序列长度 \(k\le n\) 。
- \(a_k\le m\)。
- \(\forall 1\le i\le k-1\) ,有 \(a_i\mid a_{i+1}\) 。
数据范围
- \(1\le T\le 10^3,1\le n,m\le 10^9\) 。
保证 \(\max(n,m)\gt 10^8\) 的数据至多 \(1\) 组, \(\max(n,m)\gt 10^6\) 的数据至多 \(10\) 组, \(\max(n,m)\gt 10^3\) 的数据不超过 \(50\) 组。
时间限制 \(\texttt{6s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
如果直接枚举 \(a_k\) 然后推式子,然后就会像博主一样发现式子没法化简,于是进了死胡同。。。
注意到 \(a_i\) 大部分是相同的,不同的值不超过 \(\log m\) 个。
因此,补充定义 \(a_0=1\) ,令 \(b_i=\frac{a_i}{a_{i-1}}\) ,问题转化为对序列 \(b\) 计数。
由于 \(b\) 中 \(\gt 1\) 的项不超过 \(\log m\) ,容易想到先对这些项按照相对位置 \(\texttt{dp}\) ,最后将 \(1\) 插入其中,乘上一个组合数的系数即可。
\(dp_{i,j}\) 表示填了 \(i\) 个 \(\gt 1\) 的数, \(\prod_{k=1}^ib_k=j\) 的方案数。
接下来又是一个套路:把第二维记成 \(\lfloor\frac mj\rfloor\) 。从而把第二维的状态数从 \(m\) 压到 \(2\sqrt m\) 。
转移方程 \(dp_{i-1,j}\to dp_{i,\lfloor\frac jk\rfloor}\) ,同样可以整除分块优化。
每一层的转移代价:
但是 \(\mathcal O(\log m\cdot m^\frac 34)\) 仍然过不去。
还有最后一个小优化,由于每一轮至少除以 \(2\) ,所以第 \(i\) 轮的枚举上界为 \(\frac m{2^{i-1}}\) 。
时间复杂度降至:
可以通过,注意为了卡常需要对这 \(2\sqrt m\) 个数预处理整除分块。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=1e5,mod=1e9+7;
int m,n,t,sq,cnt,res;
int c[maxn],id[2][maxn];
int dp[30][maxn];
vector<pii> vec[maxn];
int qpow(int a,int k)
{
int ans=1;
while(k)
{
if(k&1) ans=1ll*ans*a%mod;
a=1ll*a*a%mod,k/=2;
}
return ans;
}
int get_id(int x)
{
return x<=sq?id[0][x]:id[1][m/x];
}
void add(int &x,long long y)
{
x=(x+y)%mod;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m),sq=sqrt(m),cnt=0;
for(int l=1,r=0;l<=m;l=r+1) r=m/(m/l),c[++cnt]=m/l;
sort(c+1,c+cnt+1);
for(int i=1;i<=cnt;i++)
{
int x=c[i];
if(x<=sq) id[0][x]=i;
else id[1][m/x]=i;
vec[i].clear();
for(int l=2,r=0;l<=x;l=r+1)
{
r=x/(x/l);
vec[i].push_back(mp(get_id(x/l),r-l+1));
}
}
for(int j=1;j<=cnt;j++) dp[0][j]=0;
dp[0][get_id(m)]=1,res=n;///b[i]全为1的序列有n个
for(int i=1,v=n+1;1<<i<=m;i++)
{///v=c(n+1,i+1)
v=v*(n+1ll-i)%mod*qpow(i+1,mod-2)%mod;
for(int j=get_id(m>>i);j>=1;j--) dp[i][j]=0;
for(int j=get_id(m>>(i-1));j>=1;j--)
for(auto p:vec[j])
add(dp[i][p.fi],1ll*dp[i-1][j]*p.se);
for(int j=get_id(m>>i);j>=1;j--) add(res,1ll*dp[i][j]*v);
}
printf("%d\n",res);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16567405.html
浙公网安备 33010602011771号