1. 程式人生 > 實用技巧 >計數基礎題目選做

計數基礎題目選做

只是基礎題目

T1

AGC002F

首先直接當成有 \(n\) 個白球做

這題目轉移還是比較套路的

\(f_{i,j}\) 表示 \(i\) 個白球,\(j\) 個顏色的方案數

轉移直接分為兩種情況:

直接新增一個白球和新增一種顏色

新增白球直接新增即可,即

f[i][j]=add(f[i][j],f[i-1][j]);

然後直接找空位組合數新增就好了,即

f[i][j]=add(f[i][j],f[i][j-1]*(n-j+1)%mod*C(n*k-i-1-(j-1)*(k-1),k-2)%mod);

關於為啥後面的是 \(k-2?\)

首先剩下了 \(k-1\) 個帶顏色的,然後欽定第一個位置是當前顏色,否則會記重

最後答案 \(f_{n,n}\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,a,b) for(register int i=a;i<=b;++i) 
namespace yspm{
    inline int read()
    {
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int mod=1e9+7,N=4e6+10;
    int fac[N],inv[N],n,k,f[2010][2010];
    inline int ksm(int x,int y)
    {
        int res=1; for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod; 
        return res;
    }
    inline int C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    signed main()
    {
        fac[0]=1; inv[0]=1; 
        For(i,1,N-1) fac[i]=fac[i-1]*i%mod;
        inv[N-1]=ksm(fac[N-1],mod-2); 
        for(int i=N-2;i>=1;--i) inv[i]=inv[i+1]*(i+1)%mod;
        n=read(); k=read();
        for(int i=0;i<=n;++i) f[i][0]=1;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=i;++j)
            {
                f[i][j]=add(f[i][j],f[i-1][j]);
                f[i][j]=add(f[i][j],f[i][j-1]*(n-j+1)%mod*C(n*k-i-1-(j-1)*(k-1),k-2)%mod);
            }
        } printf("%lld\n",f[n][n]);
        return 0;
    }
}
signed main(){return yspm::main();}

T2

AGC001E BBQ

這題數形結合好

考慮每個 \(i,j\) 的本質

是在一個平面直角座標系裡面從\((-a_i,-b_i)\)\((a_j,b_j)\) 的方案

然後把所有的撒到一個平面直角座標系裡面然後做轉移

最後減掉那些自己轉移到自己的

#include<bits/stdc++.h>
using namespace std;
namespace yspm{
    inline int read()
    {
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int M=2e5+10,N=4040,mod=1e9+7,s=2010;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int del(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int id1(int x){return x+s;}
    inline int id2(int x){return s-x;}
    int a[M],b[M],f[N][N],inv[N],fac[N],ans,n;
    inline int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
    inline int ksm(int x,int y){int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
    signed main()
    {
        fac[0]=1; inv[0]=1; for(int i=1;i<N;++i) fac[i]=1ll*fac[i-1]*i%mod;
        inv[N-1]=ksm(fac[N-1],mod-2); for(int i=N-2;i>=1;--i) inv[i]=1ll*inv[i+1]*(i+1)%mod;
        n=read(); 
        for(int i=1;i<=n;++i) 
        {
            a[i]=read(),b[i]=read();
            ++f[id2(a[i])][id2(b[i])];
        } 
        for(register int i=1;i<=s*2;++i)
        {
            for(register int j=1;j<=s*2;++j)
            {
                f[i][j]=add(f[i][j],f[i-1][j]);
                f[i][j]=add(f[i][j],f[i][j-1]);
            }
        }
        for(int i=1;i<=n;++i) ans=add(ans,f[id1(a[i])][id1(b[i])]),ans=del(ans,C(2*(a[i]+b[i]),2*a[i]));
        ans=1ll*ans*inv[2]%mod; cout<<ans<<endl;
        return 0;
    }
}
signed main(){return yspm::main();}

T3

[MtOI2018]情侶?給我燒了! 的加強版

先用高考數學的知識推出來這樣一個式子

\[\binom n i\times \binom n i\times 2^i \times i!\times g_{n-k} \]

\(g_i\) 表示 \(i\) 對情侶的錯排方案

然後推這個錯排的式子

按照普通錯排的做法

欽定第一排是錯誤的,這裡要乘上 \(2n\times (2n-2)\)

然後考慮剩下的方案

兩對情侶中剩下的兩個人不坐在一起,那麼是一個 \(g_{i-1}\)

反之則為 \(2\times(i-1)\times g_{i-2}\)

(選擇一個座位)

所以就可以 \(O(n)\) 預處理,然後可以 \(O(1)\) 回答

#include<bits/stdc++.h>
using namespace std;
#define For(i,a,b) for(register int i=a;i<=b;++i) 
namespace yspm{
    inline int read()
    {
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int mod=998244353,N=5e6+10;
    int inv[N],fac[N],g[N],p[N],n,k;
    inline int C(int n,int m){return 1ll*inv[m]*fac[n]%mod*inv[n-m]%mod;}
    inline void work(int n,int k)
    {
        printf("%lld\n",1ll*C(n,k)*C(n,k)%mod*fac[k]%mod*p[k]%mod*g[n-k]%mod);
        return ;
    }
    inline int ksm(int x,int y)
    {
        int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod;
        return res;
    }
    signed main()
    {
        fac[0]=inv[0]=p[0]=1; g[0]=1;
        For(i,1,N-1) fac[i]=1ll*fac[i-1]*i%mod,p[i]=1ll*p[i-1]*2%mod;
        For(i,2,N-1) g[i]=1ll*2*i%mod*(2*i-2)%mod*((1ll*2*(i-1)%mod*g[i-2]%mod+g[i-1])%mod)%mod;
        inv[N-1]=ksm(fac[N-1],mod-2); for(int i=N-2;i>=1;--i) inv[i]=1ll*inv[i+1]*(i+1)%mod;
        int T=read(); 
        while(T--)
        {
            n=read();
            for(int i=0;i<=n;++i) work(n,i); 
        }
        return 0;
    }
}
signed main(){return yspm::main();}

程式碼粘的是 \(Luogu\) 普通版的