洛谷3861八月月赛A题解

链接

用f[i][j]表示乘积为i的,包含的最大数小于等于j时的方案总数

我们先考虑所用的数为1到n的情况

最后的答案就是f[n][n]-1

转移时考虑f[i][j]可以转移到的状态

显然f[i][j]可以转移到f[i*k][k](k>j),可以转移到f[i][k](k>j)

我们在考虑,可以发现只有i,j都为n的因数时才会对答案产生贡献

而1e12中因数最多的数有大约7000个因数,所以我们就可以愉快的O(7000^2)水过去了

# include<iostream>
# include<cstdio>
# include<cstring>
# include<cmath>
# include<algorithm>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int mn = 8005;
LL f[mn][mn],a[mn],n;
int cnt,t;
void work(LL x)
{
    LL m=sqrt(x*1.0);
    for(int i=1;i<=m;i++)
    {
        if(x%i==0)
        {
            if(1ll*i*i==x)
                a[++cnt]=i;
            else {
                a[++cnt]=i;
                a[++cnt]=x/i;
            }
        }
    }
    sort(a+1,a+1+cnt);
    a[cnt+1]=x+5;//增加虚拟节点
    f[1][1]=1;
    //printf("%d",cnt);
    for(int i=1;i<=cnt;i++)
    {
        int l=i+1;
        for(int j=1;j<cnt;j++)
        {
            if(f[i][j]==0) continue;
            if(a[i]*a[j+1]<=x)
            {
                while(a[l+1]<=a[i]*a[j+1]) l++;
                if(a[l]==a[i]*a[j+1])
                    f[l][j+1]=(f[l][j+1]+f[i][j])%mod;
            }
            f[i][j+1]=(f[i][j+1]+f[i][j])%mod;
        }
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        cnt=0;
        //memset(f,0,sizeof(f));
        scanf("%lld",&n);
        work(n);
        printf("%lld\n",f[cnt][cnt]-1);
        if(t)
        {
            for(int i=1;i<=cnt;i++)
                for(int j=1;j<=cnt;j++)
                    f[i][j]=0;
        }
    }
    return 0;
}

 

posted @ 2018-09-12 19:31  logeadd  阅读(225)  评论(0编辑  收藏  举报