• 组合数学专题


    NC19788 Travel

    题意为:将n个节点的树分成m个连通块,并对每个连通块标号的方案数。

    思路:初看这道题像树形dp,但一想转移方程和数据范围,感觉不可解。
    换成组合数学的角度,将分成m个连通块转化为删去m-1条边,显然删去m-1条边的方案与分成m个连通块的方案是一一对应的,再将连通块标号,就是乘m的阶乘了。
    C(n-1,m-1)×m!

    NC50039 kotori

    题意:n个1-m之间的数排成一排,相邻数不能相等的方案数。

    思路:直接给公式:m×pow(m-1,n-1)

    关键是扩展:n个1-m之间的数排成一个圆,相邻数不能相等的方案数。

    同样的断链成环

    https://www.luogu.com.cn/problem/P1357

    #include <iostream>
    #include <cstring>
    #define inf 2147483647
    #define ll long long
    using namespace std;
    
    const ll mo=1e9+7;
    ll n,m,k,N; 
    ll ans[33][33],w[33][33],a[33][33];
    //f0[i][j]:首项是0,到了第i项时为j的条件方案数。 
    
    
    void mi()
    {
    	ll kp[33][33]={0};
    	for (int i=0;i<N;i++)	for (int j=0;j<N;j++) {
    		kp[i][j]=0;
    		for (int k=0;k<N;k++) kp[i][j]=(kp[i][j]%mo+ans[i][k]*w[k][j]%mo)%mo;
    	}
    	for (int i=0;i<N;i++)
    	for (int j=0;j<N;j++) ans[i][j]=kp[i][j];
    }
    
    void zc()
    {
    	ll kp[33][33]={0};
    	for (int i=0;i<N;i++) for (int j=0;j<N;j++)
    	{	kp[i][j]=0;
    		for (int k=0;k<N;k++) kp[i][j]=(kp[i][j]%mo+w[i][k]*w[k][j]%mo)%mo;
    	}
    	for (int i=0;i<N;i++)
    	for (int j=0;j<N;j++) w[i][j]=kp[i][j];
    }
    
    void ksm(ll k)
    {
    	for (int i=0;i<N;i++) ans[i][i]=1; 
    	while (k){ if (k&1) mi(); k/=2; zc(); }
    }
    
    int check(int x)  //初赛学到的可快速算二进制中1的函数. 
    {	int sm=0;
    	while (x){ x= x&(x-1); sm++; }
    	return sm;
    }
    
    int main()
    {
    	cin>>n>>m>>k; N=(1<<m);
    	for (int i=0;i<N;i++) if (check(i)<=k)  //预处理构造矩阵 
    	{
    		int j=i>>1; w[j][i]=1; 
    		j|=(1<<(m-1)); if (check(j)<=k) w[j][i]=1;
    	}
    	ksm(n); ll sm=0;
    	for (int i=0;i<N;i++) 
    		if (check(i)<=k) sm=(sm+ans[i][i])%mo;
    	cout<<sm<<endl;
    }
    
    

    NC14735 美丽的项链

    就是一个基础dp

    #include<stdio.h>
    #include<string.h>
    const int maxn=100;
    #define max(a,b) a>b?a:b
    int n,m,a[maxn],l[maxn],r[maxn];
    long long dp[maxn][maxn+1];
    int main(){
        int i,j,k;
        //freopen("input.txt","r",stdin);
        while(~scanf("%d%d",&n,&m)){
            for(i=0;i<n;i++) scanf("%d%d",l+i,r+i);
            memset(dp,0,sizeof(dp));
            for(i=l[0];i<=r[0];i++) dp[0][i]=1;
            for(i=1;i<n;i++)
                for(j=1;j<=m;j++){
                    int left=max(0,j-r[i]),right=max(0,j-l[i]);
                    for(k=left;k<=right;k++) dp[i][j]+=dp[i-1][k];
                }
            printf("%lld\n",dp[n-1][m]);
        }
    }//dp[i][j]表示用前i种宝石组成j颗宝石的项链的方案数
     //但是第i种只能是l[i]到r[i]之间的数量
     //所以dp[i][j]=dp[i-1][j-r[i]]+dp[i-1][j-r[i]+1]+...+dp[i-1][j-l[i]]
    
    

    NC15251 白兔的式子

    NC14599 子序列

    NC15550 箱庭的股市

    NC16543 NC20824

    NC15077

    https://ac.nowcoder.com/acm/problem/15077

    因为要保证一定经过该点 所以将路线图分为两部分的卡特兰数

    这个是我高中数学老师张刚讲组合数的时候讲的方法 我印象很深

    NC16537

    【C++】球盒问题总结(八种情况)https://blog.csdn.net/Ljnoit/article/details/102211595

    namespace CNM {//组合数板子
        const int N = 2e6 + 5;
        ll quick(ll x, ll n)
        {
            ll res = 1;
            while (n)
            {
                if (n & 1) res = (res*x) % mod;
                x = x * x%mod;
                n >>= 1;
            }
            return res;
        }
        ll inv(ll x) { return quick(x, mod - 2); }
        ll fac[N], invfac[N];
        void init()
        {
            fac[0] = 1;
            for (int i = 1; i < N; ++i) fac[i] = (fac[i - 1] * i) % mod;
            invfac[N - 1] = inv(fac[N - 1]);
            for (int i = N - 2; i >= 0; --i) invfac[i] = (invfac[i + 1] * (i + 1)) % mod;
        }
        ll C(int n, int m)
        {
            if (n < m || m < 0) return 0;
            return fac[n] * invfac[m] % mod*invfac[n - m] % mod;
        }
    }
    vector<pair<int,int>> fac[MAXN];
    int x, y;
    void slove() {
        cin >> x >> y;
        ll ans = 1;
        for (auto p : fac[x]) {
            int n = p.second, m = y;
            ans *= CNM::C(n + m - 1,m - 1);
            ans %= mod;
        }
        ans *= ksm(2, y - 1, mod);
        ans %= mod;
        cout << ans << endl;
    }
    signed main() {
        CNM::init();
        for (int i = 2; i < MAXN; i++) {
            if (fac[i].size())continue;
            for (int j = i; j < MAXN; j += i) {
                int t = j, cnt = 0;
                while (t%i == 0)t /= i, cnt++;
                fac[j].push_back({ i,cnt });
            }
        }
        IOS;
        int T; cin >> T;
        while(T--)slove();
    }
    

    https://ac.nowcoder.com/acm/problem/14599

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #define int long long
    const int N     = 1e6+10;
    const int MOD   = 1000000007;
    using namespace std;
     
    long long bin[N];
    void Init()
    {
        bin[0]=1;
        bin[1]=1;
        for(int i=2;i<N;i++)
            bin[i]=i*bin[i-1]%MOD;
    }
    long long POW(long long a,long long b)
    {
        long long res=1;
        while(b)
        {
            if(b&1)
            {
                res*=a, res%=MOD;
            }
            b>>=1;
            a*=a, a%=MOD;
        }
        return res;
    }
    long long C(long long n, long long m)//求C(n,m) n在下,m在上。注意在这之前加init函数
    {
        return (bin[n]%MOD)*(POW(bin[m]*bin[n-m]%MOD,MOD-2))%MOD;
    }
     
    char str[N];
    int n, m;
     
    void Solve()
    {
        int ans = 0;
        for (int i=1; i<=m-n+1; i++)
        {
            ans += C(m-i,n-1) * POW(25, m-i-n+1) % MOD * POW(26, i-1) % MOD, ans%=MOD;
        }
        printf("%lld\n",ans);
    }
     
    signed main()
    {
        Init();
        scanf("%s",str+1);
        n = strlen(str+1);
        scanf("%lld",&m);
        Solve();
        return 0;
    }
    

    牛客33187K多校 - Link with Bracket Sequence I

       dp[0][0][0] = 1;
    	for (int i=1; i<=m; i++)
    	{
    		for (int j=0; j<=i; j++)
    		{
                //从你决定开始匹配S第一个字符之前,放什么都行(只要左括号数量>=右括号)
    			dp[i][0][j] += (j-1<0?0:dp[i-1][0][j-1]) + dp[i-1][0][j+1], dp[i][0][j]%=MOD;
    		}
    	}
    	
    	for (int i=1; i<=m; i++)
    	{
    		for (int j=1; j<=min(m, n); j++)
    		{
    			for (int k=0; k<=i; k++)
    			{
    				int d = -1 + 2*(str[j]=='(');//左括号为1,右括号为-1
    
                //对于T的第 i 个字符,有两种情况:
                //一种是第i个字符匹配上原串的第j个字符,有1种放法
                //一种是第i个字符在前i-1个已经匹配上原串的第j个字符的情况下,有1种放法。
    				if(k-d>=0)	dp[i][j][k] += dp[i-1][j-1][k-d];
    				if(k+d<=m)	dp[i][j][k] += dp[i-1][j][k+d];
    				dp[i][j][k] %= MOD;
    			}
    		}
    	}
    	printf("%lld\n",dp[m][n][0]);
    
    
  • 相关阅读:
    搭建docker镜像仓库(一):使用registry搭建本地镜像仓库
    C++ quick sort
    Ubuntu C++ uuid_generate vs Windows UuidCreate
    今天做错的笔试题:StringBuffer引用传参
    一般报java.lang.NullPointerException的原因有以下几种
    has a / is a 的区别
    1、一日一程序之C语言的Hanoi问题
    Java enum的用法详解
    OpenGL ES EAGLContext 和 EGLContext
    Windows OpenGL ES 图像对比度调节
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/16528066.html
Copyright © 2020-2023  润新知