• 【简单计数知识】JZOJ6395. 【NOIP2019模拟2019.10.28】消失的序列


    Description

    传送门

    • 求有多少个n排列可以被一下的栈排序排成升序,其中有一个元素已经确定
    • 每一次可以对序列进行入栈和出栈两种操作。
    • 入栈操作:如果序列非空,那么三叶会取出序列的第一个元素,把它扔进栈的顶部。
      出栈操作:如果栈非空,那么三叶可以取出栈的顶部,把这个元素接在结果序列的末尾。
    • 注意结果序列和原序列是不一样的,也就是出栈操作不是把元素接在原序列后。
      如果所有元素都经过了一次入栈和出栈操作,得到的结果序列为升序,那么这个序列就被排好序了。
    • n<=1e6

    Solution

    • 题解很迷,我很菜,不会怎么推,题解的方法运用看似思路清晰的DP,找出性质,即不存在大小关系为(2,3,1)的三元组,设f[i][j]表示从后往前倒i,当前加入的最小值为j。然后经过一系列的优化,这里就不再赘述。
    • 然而实际上有一种更加巧妙并且简明的做法(%%%lyl)。
    • 看到栈,我们可以考虑将序列转化为一个括号序。
    • 其中左括号表示在栈中加入一个元素,右括号表示弹栈。
    • 根据最后是升序的,我们可以确定,第i个右括号代表的就是数字i。
    • 那么与其匹配的左括号就是它加入的顺序了。
    • 如果pos位置固定为x,那么x的左括号就是第pos个,枚举它出现在2*n的括号序中的第i个,可以把x对应的右括号所在的位置推出来。
    • 剩下的贡献就是一个p个左括号,q个右括号,序列合法的个数。

    有关括号排列

    • n对括号排列数是卡特兰数C2nnn+1frac{C_{2n}^{n}}{n+1}
    • 入栈出栈的排列也是这个。
    • 那x个左括号,y个右括号呢?
    • 考虑n对括号相当于从(0,0)走到(n,n),不经过(i,i+1),将因为可以将向右看做一个左括号,向左看做一个右括号。
    • x个左括号,y个右括号也是一样的。
    • 而不经过直线y=x+1的方案可以考虑,将(x,y)终点作y=x+1对称点,那么(0,0)到(x’,y’)的方案一定唯一对应一种不合法的方案。即从第一个接触y=x+1直线的地方开始翻折过去就唯一对应了。
    • 这样就是两个组合数相减了。

    有关组合数求和

    • i=0mC(n+i,i)=C(n+1,m+1)sum_{i=0}^{m}C(n+i,i)=C(n+1,m+1).即一条斜线上的组合数是右下角一个位置的组合数。
    • 因为将杨辉三角形画出来:
      1
      1 1
      1 2 1
      1 3 3 1
      1 4 6 4 1
      1 5 10 10 5 1
    • 将每一斜排横过来:
      1 1 1 1 1 .
      1 2 3 4 5 …
      1 3 6 10 …
      1 4 10 …
      1 5 …
      1…
    • 这其实是对上一行(也就是上一斜排)求一次前缀和,根据C(n,m)=C(n-1,m)+C(n-1,m-1)推来也是一样的。
    • 那么自然一排就是右下的那个了。
    • 同理也有i=mnC(i,m)=C(n+1,m+1)sum_{i=m}^{n}C(i,m)=C(n+1,m+1)
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 2000005
    #define ll long long
    #define mo 1000000007
    using namespace std;
    
    int n,pos,x,i,j,k;
    ll fct[maxn],ift[maxn],ans;
    
    ll ksm(ll x,ll y){
    	ll s=1;
    	for(;y;y/=2,x=x*x%mo) if (y&1)
    		s=s*x%mo;
    	return s; 
    }
    
    ll C(int n,int m){
    	if (m<0||n<m) return 0;
    	return fct[n]*ift[m]%mo*ift[n-m]%mo;
    }
    
    ll F(int i,int j){
    	if (i<0||j<0) return 0; 
    	if (i<j) return 0;
    	return C(i+j,i)-C(i+j,j-1);	
    }
    
    int pd(int x){return 0<=x&&x<=2*n;}
    
    int main(){
    	scanf("%d%d%d",&n,&pos,&x);
    	fct[0]=1;for(i=1;i<maxn;i++) fct[i]=fct[i-1]*i%mo;
    	ift[maxn-1]=ksm(fct[maxn-1],mo-2);
    	for(i=maxn-2;i>=0;i--) ift[i]=ift[i+1]*(i+1)%mo;
    	for(i=1;i<=2*n;i++) {
    		int x0=pos-1,y0=i-pos;
    		int x1=x-1-y0,y1=x-1-y0;
    		j=i+x1+y1+1,k=2*n-j;
    		int x2=n-x0-x1-1,y2=n-y0-y1-1;
    		ans+=F(x0,y0)*F(x1,y1)%mo*F(y2,x2)%mo;
    	}
    	printf("%lld",(ans%mo+mo)%mo);
    }
    
    
  • 相关阅读:
    编辑器、编译器、文件、IDE等常见概念辨析
    二叉排序树的实现
    模板
    Codeforces Round #690 (Div. 3)
    Educational Codeforces Round 100 (Rated for Div. 2)
    Codeforces 414-B. Mashmokh and ACM(数位dp)
    Codeforces 339D-Xenia and Bit Operations(线段树)
    Educational Codeforces Round 96 (Rated for Div. 2) 题解
    2019-2020 ICPC Northwestern European Regional Programming Contest (NWERC 2019)
    2018-2019 ICPC Northwestern European Regional Programming Contest (NWERC 2018)
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090932.html
Copyright © 2020-2023  润新知