• BZOJ4402 Claris的剑, 【2020六校联考NOIP #1】山水画


    题目来源:BZOJ4402 Claris的剑,后被选入【2020六校联考NOIP 第1场】,题为【山水画】,搬题人memset0。

    算法:组合计数。

    题目链接

    “本质不同”这个要求比较毒瘤,我们必须先把它解决,以免重复计数。

    发现每个数列,通过重新排列,一定能转化为如下两种形式之一:

    • (1,(2,1),(2,1),dots,2,(3,2),(3,2),dots,3,(4,3),(4,3),dots,j)
    • (1,(2,1),(2,1),dots,2,(3,2),(3,2),dots,3,(4,3),(4,3),dots,j,j-1)

    其中(j)是序列里最大的数。

    至于为什么所有数列一定能转化成这样,你只需要在知道每种值的出现次数以后,让字典序尽可能小,就一定会排成这个形式。

    通过我打的括号,大家不难看出,如果把每种值第一次出现的位置拎出来,它们一定是一个恰为(1,2,dots ,j)的子序列(也就是序列里没有括号的部分)。而其它的位置,就是在这个子序列中,插入了若干个二元组。如果知道了序列长度(i)和最大元素(j),则这样的序列数量,就相当于把(lfloorfrac{i-j}{2} floor)个元素,放入(j-1)个本质不同的盒子,盒子可以为空,方案数是({lfloorfrac{i-j}{2} floor+j-1-1choose j-1-1})

    注:把(x)个本质相同的物品放入(y)个本质不同的盒子,盒子可以为空。这个方案数可以用插板法求出。先在每个盒子里补一个物品,让所有盒子都不为空。然后插板,方案数是:({x+y-1choose y-1})

    于是可以得到一个复杂度(O(nm))的做法:枚举(i,j),则答案是:

    [ ext{ans}=1+sum_{i=2}^{n}sum_{j=2}^{min(m,i)}{lfloorfrac{i-j}{2} floor+j-2choose j-2} ]

    其中最前面的(1),就是只有一个元素的序列({1}),为了避免组合数中出现负数,我们将其单独计算。

    发现这个式子里,(i,j)纠缠在一起,很难直接计算。考虑枚举(k=lfloorfrac{i-j}{2} floor)的值。则答案又可以写成:

    [egin{align} ext{ans}&=1+sum_{j=2}^{min(m,n)}sum_{k=0}^{lfloorfrac{n-j}{2} floor}{k+j-2choose j-2} +sum_{j=2}^{min(m,n-1)}sum_{k=0}^{lfloorfrac{n-j-1}{2} floor}{k+j-2choose j-2}\ &=1+sum_{j=0}^{min(m-2,n-2)}sum_{k=j}^{lfloorfrac{n-j-2}{2} floor+j}{kchoose j} +sum_{j=0}^{min(m-2,n-3)}sum_{k=j}^{lfloorfrac{n-j-3}{2} floor+j}{kchoose j}\ &=1+sum_{j=0}^{min(m-2,n-2)}{lfloorfrac{n-j-2}{2} floor+j+1choose j+1}+sum_{j=0}^{min(m-2,n-3)}{lfloorfrac{n-j-3}{2} floor+j+1choose j+1} end{align} ]

    注:最后一步,是用到了(sum_{i=x}^{y}{ichoose x}={y+1choose x+1})。可以用杨辉三角形来理解:相当于求杨辉三角上连续若干行,每行的第(x)个数的和(画出来是一条斜向左下的斜线)。从上到下把每两个数合并为下一行的第(x+1)个数即可。也可以用组合意义来理解:从(y+1)个数里选(x+1)个数,枚举最后一个数在哪,然后在前面随便选。

    通过预处理阶乘和逆元,求组合数是(O(1))的,因此最后的时间复杂度为(O(m))(n,m)同阶)。

    参考代码:

    //problem:BZOJ4402
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
    template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
    
    const int MAXN=2e6;
    const int MOD=1e9+7;
    inline int mod1(int x){return x<MOD?x:x-MOD;}
    inline int mod2(int x){return x<0?x+MOD:x;}
    inline void add(int& x,int y){x=mod1(x+y);}
    inline void sub(int& x,int y){x=mod2(x-y);}
    inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
    
    int fac[MAXN+5],ifac[MAXN+5];
    inline int comb(int n,int k){
    	if(n<k)return 0;
    	return (ll)fac[n]*ifac[k]%MOD*ifac[n-k]%MOD;
    }
    void facinit(int lim=MAXN){
    	fac[0]=1;
    	for(int i=1;i<=lim;++i)fac[i]=(ll)fac[i-1]*i%MOD;
    	ifac[lim]=pow_mod(fac[lim],MOD-2);
    	for(int i=lim-1;i>=0;--i)ifac[i]=(ll)ifac[i+1]*(i+1)%MOD;
    }
    
    int main() {
    	facinit();
    	int n,m;
    	cin>>n>>m;
    	int ans=1;
    	/*
    	for(int i=1;i<=n;++i){
    		for(int j=2;j<=m && j<=i;++j){
    			add(ans, comb((i-j)/2+j-1-1, j-1-1)); // (i-j)/2个东西,放进j-1个盒子,可以为空
    		}
    	}
    	*/
    	for(int j=0;j<=min(m-2,n-2);++j){
    		add(ans, comb((n-j-2)/2+j+1, j+1));
    	}
    	for(int j=0;j<=min(m-2,n-3);++j){
    		add(ans, comb((n-j-3)/2+j+1, j+1));
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    20200824
    Redis
    vs2012快捷键教程
    OpenCV 2.4+ C++ SVM介绍
    隐马尔科夫模型
    hu矩
    手势跟踪
    混合高斯模型GMM
    排序算法
    聚类——混合高斯模型 Gaussian Mixture Model
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13644741.html
Copyright © 2020-2023  润新知