【学习笔记】[ABC236Ex] Distinct Multiples

典中典题目。

考虑怎么转化成普通的容斥形式🤔

发现可以将 S S S拆分成若干个集合相加的形式,然后分开求 lcm \text{lcm} lcm算贡献。

感觉和斯特林数有关系。

破防了,题解告诉我们这个容斥系数是 ( − 1 ) ∣ S ∣ − 1 ( ∣ S ∣ − 1 ) ! (-1)^{|S|-1}(|S|-1)! (1)S1(S1)!😅

可以通过对边集容斥得到这个优美的结果。

可以参考洛谷题解。

发现可以将 a i = a j a_i=a_j ai=aj的两个点之间连边,这样我们转化成了图计数的问题。

C ( E ) C(E) C(E)表示连上边集 E E E后的连通块结果, F ( P ) F(P) F(P)表示连通块为 P P P时的答案,也就是连通块方案数的乘积。

那么根据经典的容斥原理,答案是 ∑ E ( − 1 ) ∣ E ∣ F ( C ( E ) ) \sum_{E}(-1)^{|E|}F(C(E)) E(1)EF(C(E))

改变求和顺序得到: ∑ P F ( P ) ∑ C ( E ) = P ( − 1 ) ∣ E ∣ \sum_{P}F(P)\sum_{C(E)=P}(-1)^{|E|} PF(P)C(E)=P(1)E

首先要做过这道题 [集训队作业2013] 城市规划

G ( x ) G(x) G(x)表示简单无向图的 E G F EGF EGF F ( x ) F(x) F(x)表示简单连通图的 E G F EGF EGF,发现 G ( x ) = ∑ n = 0 ∞ 2 ( n 2 ) x n n ! ( ∑ i = 0 ( n 2 ) ( − 1 ) i ( ( n 2 ) i ) ) = 1 + x G(x)=\sum_{n=0}^{\infty}2^{\binom{n}{2}}\frac{x^n}{n!}(\sum_{i=0}^{\binom{n}{2}}(-1)^i\binom{\binom{n}{2}}{i})=1+x G(x)=n=02(2n)n!xn(i=0(2n)(1)i(i(2n)))=1+x G ( x ) = 1 + F ( x ) 1 ! + F ( x ) 2 2 ! + . . . + F ( x ) n n ! = exp(F(x)) G(x)=1+\frac{F(x)}{1!}+\frac{F(x)^2}{2!}+...+\frac{F(x)^n}{n!}=\text{exp(F(x))} G(x)=1+1!F(x)+2!F(x)2+...+n!F(x)n=exp(F(x)),手算便知 F ( x ) F(x) F(x),这样我们得到了容斥系数。最后 D P DP DP即可。

暴力实现,复杂度 O ( 3 n ) O(3^n) O(3n)

为什么是圆排列?我感觉有不用多项式的方法,但是我想不到。

remark \text{remark} remark 感觉对多项式的掌握还差的很远。。。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
#define inf 0x3f3f3f3f
using namespace std;
const int mod=998244353;
int n;
ll m,fac[16],a[16],f[1<<16],g[1<<16];
ll gcd(ll x,ll y){
    return y==0?x:gcd(y,x%y);
}
ll lcm(ll x,ll y){
    ll z=gcd(x,y);
    if((__int128)x/z*y>m)return m+1;
    return x/z*y;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;for(int i=0;i<n;i++)cin>>a[i];
    fac[0]=1;for(int i=1;i<n;i++)fac[i]=fac[i-1]*i%mod;
    for(int i=1;i<1<<n;i++){
        ll tmp=0;int sz=0;
        for(int j=0;j<n;j++){
            if(i>>j&1){
                sz++;
                if(tmp==0)tmp=a[j];
                else tmp=lcm(tmp,a[j]);
            }
        }
        g[i]=m/tmp%mod*fac[sz-1]%mod*((sz%2==0)?-1:1);
    }
    f[0]=1;
    for(int i=1;i<1<<n;i++){
        int k=0;while(!(i>>k&1))k++;
        for(int j=i;j;j=(j-1)&i){
            if(j>>k&1)f[i]=(f[i]+f[i-j]*g[j])%mod;
        }
    }
    cout<<(f[(1<<n)-1]+mod)%mod;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值