peiwenjun's blog 没有知识的荒原

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}\) ,同样可以整除分块优化。

每一层的转移代价:

\[\sum_{i=1}^\sqrt m\mathcal O(\sqrt i+\sqrt\frac mi)=\mathcal O(m^\frac 34)\\ \]

但是 \(\mathcal O(\log m\cdot m^\frac 34)\) 仍然过不去。

还有最后一个小优化,由于每一轮至少除以 \(2\) ,所以第 \(i\) 轮的枚举上界为 \(\frac m{2^{i-1}}\)

时间复杂度降至:

\[\mathcal O(\sum\limits_{i=1}^{\log m}(\frac m{2^{i-1}})^\frac 34)=\mathcal O(m^\frac 34)\\ \]

可以通过,注意为了卡常需要对这 \(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;
}

posted on 2022-08-09 19:54  peiwenjun  阅读(15)  评论(0)    收藏  举报

导航