[2018年最后一水]烤乐滋排队

题目描述

烤乐滋炸桥的行为被村长发现了,村长很愤怒,叫烤乐滋赔钱。可烤乐滋身上没有那么多钱,他就只好去银行取钱了。
不过你应该能够想到,作为天下养猪第一村的地方,银行肯定也是很不一般的。每个人来到银行,都有一个想要取的钱数Mi,一个排队的队是合法的,只有在你把这个队中的所有人按照Mi从小到大排序之后,每相邻的两个人当中,后一个人要取的钱数一定是前面那个人的倍数,且这两个人要取的钱数不相等。
例如:2 9 1 4 96,这个队是合法的,因为将它排序之后的1 2 4 8 96没有相邻的两个数相等,并且后面一个数总是前面一个数的倍数。
而2 8 1 3 96,这个队是不合法的,因为将它排序之后的1 2 3 8 96当中3不是2的倍数。
显然,只有1个人的序列和没有人的序列一定是合法的。
现在有n个人在排队,烤乐滋可以从中叫出来若干个人(人数可以为0..n中的任意数),使这些人按照原来在队伍中的顺序组成一个队。烤乐滋想要知道,有多少种这样的方案使得叫出来的人排起来的队是合法的。
由于这个数太大了,你需要将它对998244353取模后输出。

输入

第一行一个数n,表示排队的人数。
第二行n个数,第i个数Mi表示排在第i个的人要取的钱。

输出

方案数,对998244353取模。

dp+枚举因子+离散化

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+7;
int s[maxn];
ll dp[maxn];
const ll mod = 998244353;
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&s[i]);
        dp[s[i]]++;
    }
    ll ans=0;
    sort(s+1,s+1+n);
    int m =(int) (unique(s+1,s+n+1) -s - 1);
    for(int i=1;i<=m;++i){
        ll temp=dp[s[i]];
        for(int j=1;j*j<=s[i];++j){
            if(s[i]%j!=0||s[i]==1) continue;
            if(j*j!=s[i]){
                dp[s[i]]=(dp[s[i]]+temp*dp[j]%mod)%mod;
                if(j!=1)
                dp[s[i]]=(dp[s[i]]+temp*dp[s[i]/j]%mod)%mod;
            }
            else{
                dp[s[i]]=(dp[s[i]]+temp*dp[j]%mod)%mod;
            }
        }
        ans=(ans+dp[s[i]])%mod;
    }
    printf("%lld\n",(ans+1)%mod);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smallocean/p/10204970.html
今日推荐