zoukankan      html  css  js  c++  java
  • 【比赛记录】CF Round 745 div2

    A:

    求一个排列满足 (p_i>p_{i+1}) 的数对大于 (n) 的排列数。

    显然这玩意对称,直接 (frac{(2n)!}{2})

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    const int mod=1e9+7;
    inline int Add(int x,int y){return (x+y)%mod;}
    inline int Mul(int x,int y){return 1ll*x*y%mod;}
    inline int qpow(int x,int y){
    	int res=1;
    	while(y){
    		if(y&1)res=Mul(res,x);
    		x=Mul(x,x);y>>=1; 
    	}
    	return res;
    }
    int T;
    int n,m;
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		cin>>n;
    		n<<=1;
    		int res=1;
    		for(int i=1;i<=n;++i)res=Mul(res,i);
    		res=Mul(res,qpow(2,mod-2));
    		printf("%d
    ",res);
    	}	
    	return 0;
    }
    

    B:

    构造一张 (n)(m) 边的图满足最长路径小于 (k-1)

    显然的梅花图构造,注意一下完全图以及不连通以及重边自环即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N=2e5+10;
    const int mod=998244353;
    inline int Add(int x,int y) {
    	return (x+y)%mod;
    }
    inline int Mul(int x,int y) {
    	return 1ll*x*y%mod;
    }
    inline int qpow(int x,int y) {
    	int res=1;
    	while(y) {
    		if(y&1)res=Mul(res,x);
    		x=Mul(x,x);
    		y>>=1;
    	}
    	return res;
    }
    int T;
    int n,m,k;
    signed main() {
    	scanf("%d",&T);
    	while(T--) {
    		cin>>n>>m>>k;
    		if(k<=1){
    			puts("NO");
    			continue;
    		}
    		if(m<n-1&&n!=1) {
    			puts("NO");
    			continue;
    		}
    		if(m>(n*(n-1)/2)){
    			puts("NO");
    			continue;
    		}
    		if(m>=n-1&&k>3) {
    			puts("YES");
    			continue;
    		}
    		if(k<=3) {
    			if(k==2) {
    				if(n==1) {
    					puts("YES");
    					continue;
    				}
    				puts("NO");
    				continue;
    			}
    			if(k==3) {
    				if(m==(n*(n-1))/2) {
    					puts("YES");
    					continue;
    				} else {
    					puts("NO");
    					continue;
    				}
    			}
    			if(k<=1) {
    				puts("NO");
    				continue;
    			}
    		}
    	}
    	return 0;
    }
    

    C:

    给定矩阵求其满足四边及其左上右下角为 (1) 其它部分为 (0) 的子矩阵的最小修改次数

    听说直接 (O(n^4)) 并判断答案是否大于 (16) 就直接过了……

    考虑:先枚举两列再枚举一行。然后去确定剩下的一行。

    倒着枚举行,然后做一个前缀最大值,这样贡献就变成用一个前缀来减去后面的最大值来得到最小贡献。

    注意计算的时候要带着两个角。还有就是用前缀和来优化这个算贡献的过程,只需要维护行即可。复杂度 (O(n^3))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=500;
    int sum[N][N],n,m,T,a[N][N];
    char ch[N];
    inline int qr(int x,int y,int xx,int yy){
    	return sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1];
    }
    inline int Max(int x,int y){return x<y?y:x;}
    inline int Min(int x,int y){return x<y?x:y;}
    int f[N];
    #define Mem(a) memset(a,0,sizeof a)
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d",&T);
    	while(T--){
    		Mem(a);Mem(sum);
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;++i){
    			scanf("%s",ch+1);
    			for(int j=1;j<=m;++j)a[i][j]=ch[j]-'0';
    			for(int j=1;j<=m;++j)sum[i][j]=sum[i][j-1]+a[i][j];
    		}
    //		for(int i=1;i<=n;++i){
    //			for(int j=1;j<=n;++j){
    //				sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
    //			}
    //		}
    		int ans=(1<<30);
    		for(int i=1;i<=m-3;++i){
    			for(int j=i+3;j<=m;++j){
    				int pre=0,mx=-1e9;
    				Mem(f);
    				for(int k=n;k>=1;--k){
    					if(k+4<=n)mx=Max(mx,f[k+4]);
    					ans=Min(ans,j-i-1+pre-mx-sum[k][j-1]+sum[k][i]);
    					pre=pre+2-a[k][i]-a[k][j]+sum[k][j-1]-sum[k][i];
    					f[k]=pre-(j-i-1-sum[k][j-1]+sum[k][i]);
    				}
    			}
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    D:

    定义一个数是好的,当且仅当在这个排列中,所有包含它的子段一共有 (m) 个不同的最大值。

    现在要求,给定 (n,m,k,) 求有多少个长度为 (n) 的排列有 (k) 个好数。

    考虑一个 (dp,)(f[i][j][k]) 表示 (n=i,m=j,k=k) 的答案。

    考虑一个从最大值往最小值的填数过程。我们枚举位置,会观察到一些性质:

    比如,最大值左右两端的填数互不影响。 因为它们扩展到最大值之后的最大值就一定会是这里填的数,不再改变了。

    同时我们还能发现,这也就相当于把两边转化为了一个 (m o m-1) 的子问题

    到这里 (dp) 的模型就很显然了,但还是要捋清思路:考虑枚举位置之后,我们还需要知道左右两边分别贡献了多少个好数。

    那么转移就算 (O(n^5)) 了。考虑选择出 (l-1) 个数放在左边,然后去 (dp) 枚举左边贡献了多少好数。代码里面就是:

    int res=0;
    	for(int u=1;u<=n;++u){
    		int tmp=0;
    		for(int v=0;v<=k;++v){
    			tmp=Add(tmp,Mul(dfs(u-1,m-1,v),dfs(n-u,m-1,k-v)));
    		}
    		tmp=Mul(tmp,C(n-1,u-1));
    		res=Add(res,tmp);
    	}
    	f[n][m][k]=res;
    

    剩下的问题就在于初始化了。这也是最难理解的一部分)

    对于 (n=0) 的情况:此时如果要求我选出 (k=0) 个好数,那我是恰好有一种情况:不选的。否则方案不合法。

    对于 (n=1) 的情况:如果恰好 (m=k=1) 那也就有一种情况来选。注意,如果 (k=0,m ot =1,) 此时也有一种情况:不选,否则不合法。

    这是因为,我们现在只有可能选择出一个数,对于 (m ot =1) 的情况我们没有选择,也就恰好对应了要求;如果选择 (1) 的话也就不合法了。

    对于 (m=1) 的情况:如果 (k=1) 意味着我可以随意排列,此时一定只有一个数的 (m=1,) 也就是最大值。否则方案不合法。

    if(n==0){
    		if(k==0)return 1;
    		return 0;
    	}
    	if(n==1){
    		if(k==1&&m==1)return 1;
    		if(!k&&m!=1)return 1;//
    		return 0;
    	}
    	if(m==1){
    		if(k==1)return fac[n];
    		return 0;
    	}
    

    技巧就是用小数据拍来确定边界情况

    那么,这样是 (O(n^5)=O(10^{10})) 怎么过?

    考虑剪枝。王队长给出了一些神奇的剪枝:

    • if(k>n)return 0;

    这个就很显然了。

    • if(n-m+mm<k)return 0;

    这里 (mm) 是初始 (m) 的值。看起来好像没啥用的样子)因为 (mleq mm?)

    • if(n-m+n/2<k)return 0;

    因为 (mleq 2) 的时候最多能凑出 (n/2) 个好数,如果还是不满足直接返回 (0.)

    综上,带上火车头就可以过掉了。

    #pragma GCC optimize(3)
    #pragma GCC target("avx,sse2,sse3,sse4,mmx")
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-fwhole-program")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-fstrict-overflow")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-skip-blocks")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("-funsafe-loop-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    #include<bits/stdc++.h>
    using namespace std;
    const int N=101;
    int f[N][N][N];
    int n,k,m,mod;
    int fac[N];
    inline int Add(int x,int y){return (x+y)%mod;}
    inline int Mul(int x,int y){return 1ll*x*y%mod;}
    int Cc[101][101];
    void pre(){
    	fac[0]=1;
    	for(int i=1;i<=100;++i)fac[i]=Mul(fac[i-1],i);
    	Cc[0][0]=1;
    	for(int i=1;i<=n;++i){
    		Cc[i][0]=1;
    		for(int j=1;j<=i;++j){
    			Cc[i][j]=Add(Cc[i-1][j-1],Cc[i-1][j]);
    		}
    	}
    }
    inline int C(int x,int y){
    	return Cc[x][y];
    }
    int nn,mm,kk;
    int dfs(int n,int m,int k){
    	if(n==0){
    		if(k==0)return 1;
    		return 0;
    	}
    	if(n==1){
    		if(k==1&&m==1)return 1;
    		if(!k&&m!=1)return 1;//
    		return 0;
    	}
    	if(m==1){
    		if(k==1)return fac[n];
    		return 0;
    	}
    	if(~f[n][m][k])return f[n][m][k];
    	if(k>n)return 0;
    	if(n-m+mm<k)return 0;
    	if(n-k+n/2<k)return 0; 
    	int res=0;
    	for(int u=1;u<=n;++u){
    		int tmp=0;
    		for(int v=0;v<=k;++v){
    			tmp=Add(tmp,Mul(dfs(u-1,m-1,v),dfs(n-u,m-1,k-v)));
    		}
    		tmp=Mul(tmp,C(n-1,u-1));
    		res=Add(res,tmp);
    	}
    	f[n][m][k]=res;
    	return f[n][m][k];
    }
    signed main(){
    // 	freopen("in.txt","r",stdin);
    	scanf("%d%d%d%d",&n,&m,&k,&mod);
    	nn=n;mm=m;kk=k;
    	memset(f,-1,sizeof f);
    	//n个数,优秀值为m,好数是k个 
    	pre();
    //	cout<<C(10,1)<<endl;
    	printf("%d
    ",dfs(n,m,k));
    	return 0;
    }
    
  • 相关阅读:
    python2和python3的区别
    星球大战
    [USACO]高低卡(金)High Card Low Card (Gold)
    学习笔记
    叶子的染色
    大问题
    ...
    考试前...
    [HAOI2010]计数
    [POI2006]OKR-Periods of Words
  • 原文地址:https://www.cnblogs.com/h-lka/p/15359901.html
Copyright © 2011-2022 走看看