• 暑期集训day29 火题大战Vol.0 整理


    T1:地精部落

    题目大意:

    定义中间大临靠的两边小的山峰,反之为山谷,求出一半峰一半谷的序列个数

    思路1:暴力((20pts-40pts)

    开题一看,又是排列问题,而且

    全排列+打表搞它

    代码:

    int main(){
    	n=read(),mol=read();
    	for(int i=1;i<=n;i++)a[i]=i;
    	do{
    		int flag=1;
    		for(int i=2;i<n;i++){
    			if((a[i-1]<a[i]&&a[i]<a[i+1])||(a[i-1]>a[i]&&a[i]>a[i+1])){flag=0;break;}
    		}
    		ans+=flag;
    	}while(next_permutation(a+1,a+1+n));
    	cout<<ans%mol;
    	return 0;
    }
    
    

    思路2:(DP)(100pts)

    像这种排列方面的问题,而且是只给了数列大小的题,不是打表找规律就是贪心过样例n方DP,区别就看数据范围

    一看就是神仙做的:

    一看就是(O(1))的组合数或矩阵加速的(一般(n)直接开(1e18)了):

    一看就是(DP)的:

    放错图了:

    好了,回归正题

    一看就很组合数+(DP),设定(f_{i,0/1})表示长度为(i)且开头是山谷或山峰的合法序列。现在有以下几个性质:

    • 性质1:(1-k)的合法序列和(x+1-x+k+1)的合法序列方案数一致

      • 证明:可以直接把(x+1-x+k+1)整体减去(x),这样就得到了(1-k)的合法序列
    • 性质2:合法序列长度为奇数时,若首元素为山谷,则尾元素也为山谷;若首元素为山峰,则尾元素也为山峰。合法序列长度为偶数时,若首元素为山谷,则尾元素也为山峰;若首元素为山峰,则尾元素也为山谷

      • 证明:动手画一下就行

    所以我们可以用脚得出状态转移方程:

    [f_{i,0}=sum f_{j,0}×f{i-j-1}×C_{i-1}^j(j\%2==1) ]

    [f_{i,1}=sum f_{j,1}×f{i-j-1}×C_{i-1}^j(j\%2==0) ]

    我们考虑把第(i)个元素,即数字(i)插入到前面的序列中,可以以插入的位置为断点,考虑左右两个序列。根据性质1和性质2可以直接得出转移方程。后面组合数代表前(i-1)个元素,选出(j)个元素放在断点左边,其它的放在断点右边,因为枚举了第一个元素是山峰或山谷,只有当断点左右都是山谷时才能插入,所以转移一定合理

    代码:

    int main(){
    	n=read();mol=read();
    	c[1][0]=c[1][1]=1;
    	for(int i=2;i<=n;i++){
    		c[i][0]=1;
    		for(int j=1;j<=i;j++){
    			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mol;
    		}
    	}
    	f[0][0]=f[0][1]=f[1][0]=f[1][1]=1;	
    	for(int i=2;i<=n;i++){
    		for(int j=0;j<i;j++){
    			if(j&1)f[i][0]=(f[i][0]+f[j][0]*f[i-j-1][0]%mol*c[i-1][j]%mol)%mol;
    			else f[i][1]=(f[i][1]+f[j][1]*f[i-j-1][0]%mol*c[i-1][j]%mol)%mol;
    		}
    	}
    	cout<<(f[n][0]+f[n][1])%mol;
    	return 0;
    }
    

    思路3:优化((100pts)

    对于上面的性质,我们可以扩展出第三条:

    • (f_{i,0}=f_{i,1})
      • 证明:很简单,把原序列翻过来就行了

    所以可以直接把第二维压掉

    代码:

    int main(){
    	n=read();mol=read();
    	c[1][0]=c[1][1]=1;
    	for(int i=2;i<=n;i++){
    		c[i][0]=1;
    		for(int j=1;j<=i;j++){
    			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mol;
    		}//这里也可以滚动数组放在下面,省点空间
    	}
    	f[0]=1;f[1]=1;	
    	for(int i=2;i<=n;i++){
    		for(int j=0;j<i;j++){
    			if(j&1)f[i]=(f[i]+1LL*f[j]*f[i-j-1]%mol*c[i-1][j]%mol)%mol;
    		}
    	}
    	cout<<f[n]*2%mol;
    	return 0;
    }
    
  • 相关阅读:
    OTA JAR和JAD的mime不同
    document.getElementById('selCatalog').remove(i)突然无效???!
    判断WAP1.1和WAP2.0并解析为wml或xhtml
    IE和firefox下显示html内容
    unixrisk tip
    unixftp windows
    unixstdin/stdout/stderr
    峰鸟摄影
    linuxgrep commond
    unixtutorial(recommended)
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/13488492.html
Copyright © 2020-2023  润新知