数学专题

ARC110D Binomal Coefficient is Fun

给你一个序列 \(A\),求对于所有满足 \(\sum B_i=m\) 的序列 \(B\)\(\prod\limits_{i=1}^n\binom{B_i}{A_i}\) 的和。

\(n,A_i\le 2000,m\le 10^9\)

首先可以知道 \(\forall i,B_i\ge A_i\),不然没贡献。

\(s=\sum A_i\)。其组合意义为将 \(m\) 个球分成 \(n\) 组(相当于在 \(m+n\) 个球里选走了 \(n\) 个球),再在第 \(i\) 组里选出 \(A_i\) 个球的方案数,即 \(\binom{m+n}{s+n}\)

直接用阶乘求是 \(O(m)\) 的,考虑将原式转化为下降幂 \(\frac{(m+n)^{\underline{m-s+1}}}{(s+n)!}\),最后时间复杂度 \(O(s+n)\)

code
#include<bits/stdc++.h>
#define int long long
const int maxn=3e3+3;
const int mod=1e9+7;
using namespace std;
int n,m;
int a[maxn],fac[maxn*maxn],inv[maxn*maxn],invf[maxn*maxn];
int C(int a,int b){
    if(a<b) return 0;
    return fac[a]*invf[b]%mod*invf[a-b]%mod;
}
int qpow(int a,int b){
    int res=1;
    for(;b;a=a*a%mod,b>>=1) if(b&1) res=res*a%mod;
    return res;
}
signed main(){
    cin>>n>>m;
    inv[1]=fac[1]=1;
    for(int i=2;i<=maxn*maxn-9;i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fac[i]=fac[i-1]*i%mod;
    }
    invf[maxn*maxn-9]=qpow(fac[maxn*maxn-9],mod-2);
    for(int i=maxn*maxn-10;i;i--) 
        invf[i]=invf[i+1]*(i+1)%mod;
    int s=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        s+=a[i];
    }
    int ans=1;
    for(int i=m-s+1;i<=m+n;i++){
        ans=ans*i%mod;
    }
    cout<<ans*invf[n+s]%mod;
    return 0;
}

CF785D Anton and School

给你一个长为 \(n\) 的括号序列,求所有形如 (((...)))子序列数。

\(n\le 2\times 10^5\)

考虑枚举一个位置 \(i\),设其左边 ( 的个数为 \(L_i\),右边 ) 的个数为 \(R_i\),则包含 \(i(s_i=\texttt{(})\) 的合法子序列数为 \(\sum\limits_{m=1}^{\min(L_i,R_i)} \binom{L_i-1}{m-1}\binom{R_i}{m}\)(左边在剩下的 \(L_i-1\)( 里选 \(m-1\) 个,右边选 \(m\) 个),范德蒙德卷积一下

切记范德蒙德卷积下标从 0 开始!!!!!!!

\[\begin{aligned}\sum\limits_{m=1}^{\min(L_i,R_i)} \binom{L_i-1}{m-1}\binom{R_i}{m}&=\sum\limits_{m=0}^{\min(L_i-1,R_i-1)} \binom{L_i-1}{L_i-m-1}\binom{R_i}{m+1}\\&=\binom{L_i+R_i-1}{L_i}\end{aligned} \]

时间复杂度 \(O(n)\)

code
#include<bits/stdc++.h>
#define int long long
const int maxn=2e5+3;
const int mod=1e9+7;
using namespace std;
int n,m;
int l[maxn],r[maxn],fac[maxn],inv[maxn],invf[maxn];
int C(int a,int b){
    if(a<b) return 0;   
    return fac[a]*invf[b]%mod*invf[a-b]%mod;
}
int qpow(int a,int b){
    int res=1;
    for(;b;a=a*a%mod,b>>=1) if(b&1) res=res*a%mod;
    return res;
}
char s[maxn];
signed main(){
    cin>>(s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
        l[i]=l[i-1]+(s[i]=='(');
    for(int i=n;i;i--)
        r[i]=r[i+1]+(s[i]==')');
    inv[1]=fac[0]=fac[1]=1;
    for(int i=2;i<=n;i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fac[i]=fac[i-1]*i%mod;
    }
    invf[n]=qpow(fac[n],mod-2);
    for(int i=n-1;~i;i--) 
        invf[i]=invf[i+1]*(i+1)%mod;
    int ans=0;
    for(int i=1;i<n;i++)
        if(s[i]=='(')
            ans=(ans+C(l[i]+r[i]-1,l[i]))%mod;
    cout<<(ans?(ans-1):0);
    return 0;
}

CF1657E Star MST

有一个完全图,所有边边权在 \([1,k]\) 之间,其最小生成树权值等于所有与 \(1\) 相连的边权和,求可能的完全图数量。

\(n,k\le 250\)

\(f[i][j]\) 表示当前完全图中有 \(i\) 个点,MST 上的最大边权为 \(j\),则 \(f[i][j]\leftarrow f[i-t][j-1],t\in[0,i-1]\) 即在 \(i-t\) 个点,MST 最大边权为 \(j-1\) 的图上加上 \(t\) 个点,向 \(1\) 连边的权值为 \(j\),得到 \(i\) 个点,MST 最大边权为 \(j\) 的图。而新加的 \(t\) 个点,与其他点的连边数量为 \(w=t(i-1)-\binom{t}{2}\),每边边权要 \(\ge j\),方案数为 \((k-j+1)^{w}\)

综上有转移:

\[f[i][j]=\sum\limits_{t=0}^{i-1} \binom{i}{t}f[i-t][j-1](k-j+1)^{w} \]

时间复杂度 \(O(n^2k\log n^2)\)

code
#include<bits/stdc++.h>
#define int long long
const int maxn=250+3;
const int mod=998244353;
using namespace std;
int n,k;
int fac[maxn],inv[maxn],invf[maxn],f[maxn][maxn];
int C(int a,int b){
    if(a<b) return 0;
    return fac[a]*invf[b]%mod*invf[a-b]%mod;
}
int qpow(int a,int b){
    int res=1;
    for(;b;a=a*a%mod,b>>=1) if(b&1) res=res*a%mod;
    return res;
}
signed main(){
    cin>>n>>k; n--;
    inv[1]=fac[0]=fac[1]=1;
    for(int i=2;i<=maxn-3;i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fac[i]=fac[i-1]*i%mod;
    }
    invf[maxn-3]=qpow(fac[maxn-3],mod-2);
    for(int i=maxn-4;~i;i--) 
        invf[i]=invf[i+1]*(i+1)%mod;
    f[0][0]=1;
    for(int i=0;i<=n;i++){
        for(int j=1;j<=k;j++){
            for(int t=i;t>=0;t--){
                int w=(t*(i-1)-(t-1)*t/2);
                f[i][j]=(f[i][j]+C(i,t)*f[i-t][j-1]%mod*qpow(k-j+1,w)%mod)%mod;
            }
        }
    }
    cout<<f[n][k];
    return 0;
}

ARC001E BBQ Hard

老早就讲过这个题但是一直没做?

给你两个序列 \(A,B\),求 \(\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\binom{a_i+a_j+b_i+b_j}{a_i+a_j}\)

\(n\le 2\times 10^5,A_i,B_i\le 2000\)

前置知识:\(\binom{a+b}{a}\) 表示从 \((0,0)\) 只往右或上走到 \((a,b)\) 的方案数。

即原式代表从 \((0,0)\) 走到 \((a_i+a_j,b_i+b_j)\) 的方案数。不难发现,只要起点和终点的相对位置不变,总方案数不变。所以我们可以将起点平移到 \((-a_i,-b_i)\),终点为 \((a_j,b_j)\),即可把 \(i,j\) 的贡献分开。设 \(f[i][j]\) 表示从任意位置到达 \((i,j)\) 的方案数,则只要把所有 \((a_i,b_i)\) 加 1 作为起点,即可合并统计到达任意位置的方案数,其实原理和前缀和/二维数点差不多少。时间复杂度 \(O((A_i+B_i)^2+n)\)

code
#include<bits/stdc++.h>
#define int long long
const int mod=1e9+7;
const int maxn=2e5+3;
using namespace std;
const int N=2003;
int n,a[maxn],b[maxn];
int f[4033][4033],inv[8033],fac[8033],invf[8033];
int C(int a,int b){
    if(a<b) return 0;
    return fac[a]*invf[b]%mod*invf[a-b]%mod;
}
int qpow(int a,int b){
    int res=1;
    for(;b;a=a*a%mod,b>>=1) if(b&1) res=res*a%mod;
    return res;
}
signed main(){
    cin>>n;
    invf[1]=fac[1]=inv[1]=1;
    for(int i=2;i<=8030;i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fac[i]=fac[i-1]*i%mod;
    }
    invf[8030]=qpow(fac[8030],mod-2);
    for(int i=8029;i;i--) 
        invf[i]=invf[i+1]*(i+1)%mod;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        f[N-a[i]][N-b[i]]++;
    }
    for(int i=1;i<4030;i++){
        for(int j=1;j<4030;j++){
            f[i][j]=(f[i][j]+f[i-1][j]+f[i][j-1])%mod;
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=(ans+f[N+a[i]][N+b[i]])%mod;
        ans=(ans-C(2*a[i]+2*b[i],2*a[i])+mod)%mod;
    }
    cout<<ans*inv[2]%mod;
    return 0;
}
posted @ 2024-10-03 20:57  view3937  阅读(3)  评论(0编辑  收藏  举报
Title