题解 luoguP5216 【DLS 采花】

博客详细解析了luoguP5216题目中‘DLS 采花’问题的解决方案。通过组合数学和逆元的概念,计算每个数的因子不在其之前的排列方案数。对于每个数,其贡献为数的值乘以特定的组合数和阶乘值。文章提供了预处理阶乘逆元的方法来求解问题。
摘要由CSDN通过智能技术生成

传送门

显然每个数的贡献可以单独算,即这个数的值 × \times ×方案数。

现在的问题就是,对于一个数,有多少种排列,使得它的因子不在它之前。我们不需要知道因子的值,只需要知道个数,设为 x x x

方案特别好算,考虑算上它本身的 x + 1 x+1 x+1个数,先随便在数列中放,方案为 C n x + 1 C_n^{x+1} Cnx+1,考虑 x + 1 x+1 x+1个数中,它本身要放最前面,剩下 x x x个随便放,即 x ! x! x!,考虑剩下的 n − x − 1 n-x-1 nx1个数随便放,方案为 ( n − x − 1 ) ! (n-x-1)! (nx1)!

所以对于一个数,贡献为 a [ i ] × C n x + 1 × x ! × ( n − x − 1 ) ! a[i]\times C_n^{x+1}\times x!\times (n-x-1)! a[i]×Cnx+1×x!×(nx1)!

预处理阶乘逆元即可。

C o d e   B e l o w : Code\ Below: Code Below:

#include<bits/stdc++.h>
#define ts cout<<"ok"<<endl
#define int long long
#define hh puts("")
#define pc putchar
#define mo 998244353
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
using namespace std;
const int N=100005;
int n,a[N],tong[N],gs[N],inv[N],jc[N],ans;
inline int read(){
    int ret=0,ff=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)){ret=ret*10+(ch^48);ch=getchar();}
    return ret*ff;
}
void write(int x){if(x<0){x=-x,pc('-');}if(x>9) write(x/10);pc(x%10+48);}
void writeln(int x){write(x),hh;}
void writesp(int x){write(x),pc(' ');}
void part(int id,int x){
    for(int i=1;i*i<=x;i++){
        if(x%i==0){
            gs[id]+=tong[i];
            if(i*i!=x) gs[id]+=tong[x/i];
        }
    }
    gs[id]--;
}
int ksm(int x,int y){
    int res=1;
    while(y){
        if(y&1) res=res*x%mo;
        y>>=1;
        x=x*x%mo;
    }
    return res;
}
int C(int x,int y){
    return jc[x]*inv[y]%mo*inv[x-y]%mo;
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),tong[a[i]]++;
    for(int i=1;i<=n;i++) part(i,a[i]);
    jc[0]=1;
    for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mo;
    inv[n]=ksm(jc[n],mo-2);
    for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mo;
    for(int i=1;i<=n;i++) ans=(ans+C(n,gs[i]+1)*jc[gs[i]]%mo*jc[n-gs[i]-1]%mo*a[i]%mo)%mo;
    write(ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值