zoukankan      html  css  js  c++  java
  • 2021牛客暑期多校训练营4

    2021牛客暑期多校训练营4

    B Sample Game

    经典的期望dp,最后求得是平方可能会造成一些困扰。
    设dp[i][0]代表选择了i之后步数的期望,dp[i][1]代表选择了i之后步数的平方的期望。

    先考虑求出期望步数,枚举下次随机生成了哪一个数转移即可。
    (sum=sum w[i])
    (dp[i][0]=sum_{j>=i} [frac{w[j]}{sum}(dp[j][0]+1)]+sum_{j<i} (frac{w[j]}{sum}*1))
    将式子中的dp[i][0]放在一边
    (frac{sum-w[i]}{sum}dp[i][0]=sum_{j>i} [frac{w[j]}{sum}(dp[j][0]+1)]+sum_{j<i} (frac{w[j]}{sum}*1)+frac{w[i]}{sum}*1)
    (dp[i][0]=frac{sum}{sum-w[i]}{sum_{j>i} [frac{w[j]}{sum}(dp[j][0]+1)]+sum_{j<i} (frac{w[j]}{sum}*1)+frac{w[i]}{sum}*1})
    预处理出(sum_{j<i} (frac{w[j]}{sum}*1)),在dp过程中维护(sum_{j>=i} [frac{w[j]}{sum}(dp[j][0]+1)])整个过程就是(O(n))

    之后考虑如何求dp[i][1]
    (dp[i][1]=frac{sum num_i^2} {n})(dp[i][0]=frac{sum num_i}{n})
    (num_i)是第i种方案的步数,(n)是总方案数。
    dp[i][1]也是像dp[i][0]那样转移的,所以我们要求出dp[i][1]+1(转移时将期望步数加1)即(frac{sum (num_i+1)^2}{n})
    (frac{sum (num_i+1)^2}{n}=frac{sum (num_i^2+2num_i+1)}{n}=frac{sum num_i^2}{n}+2*frac{sum num_i}{n}+1=dp[i][1]+2dp[i][0]+1)
    最后统计答案时枚举第一个数随机生成哪一个。
    最好的复杂度是(O(n)),但是出题人给的范围是100。于是我是用(O(n^2))写的。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define p 998244353
    #define int long long
    #define N 150
    int w[N],s[N],dp[N],Sum[N];
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    int ksm(int x,int b){
    	int ans=1;
    	while(b){
    		if(b&1)ans=ans*x%p;
    		x=x*x%p;
    		b>>=1;
    	}
    	return ans;
    }
    signed main(){
    	int n=read();
    	for(int i=1;i<=n;i++)w[i]=read(),Sum[i]=(Sum[i-1]+w[i])%p;
    	for(int i=n;i>=1;i--){
    		for(int j=1;j<=n;j++){
    			int x=ksm((Sum[n]-w[i]+p)%p,p-2)%p*Sum[n]%p;
    			int y=w[j]*ksm(Sum[n],p-2)%p;
    			if(j<i){
    				s[i]=(s[i]+x*y%p)%p;
    				continue;
    			}
    			if(j==i){
    				s[i]=(s[i]+x*y)%p;
    				continue;
    			}
    			s[i]=(s[i]+x*(s[j]+1)%p*y%p)%p;
    		}
    	}
    	
    	for(int i=n;i>=1;i--){
    		for(int j=1;j<=n;j++){
    			int x=ksm((Sum[n]-w[i]+p)%p,p-2)%p*Sum[n]%p;
    			int y=w[j]*ksm(Sum[n],p-2)%p;
    			if(j<i){
    				dp[i]=(dp[i]+x*y%p)%p;
    				continue;
    			}
    			if(j==i){
    				dp[i]=(dp[i]+x*y%p*(2*s[i]+1))%p;
    				continue;
    			}
    			dp[i]=(dp[i]+x*(dp[j]+2*s[j]+1)%p*y%p)%p;
    		}
    	}
    	int ans=0;
    	for(int i=1;i<=n;i++)
    		ans=(ans+w[i]*ksm(Sum[n],p-2)%p*(dp[i]+s[i]*2+1))%p;
    	printf("%lld
    ",ans);
    	return 0; 
    }
    

    C LCS

    不妨假设(a<=b<=c)
    有这样一种构造的方法。
    先满足(a,b)。让三个串的前缀填满字符a。

    aaaxxxxxxxxxxxxxxxxxxxxx
    aaaaaxxxxxxxxxxxxxxxxxxx
    aaaaaxxxxxxxxxxxxxxxxxxx
    然后考虑满足c。
    这时让s1和s3的后缀取c-a个相等的字符

    aaaxxxxxxxxxxxbbbbbbbbb
    aaaaaxxxxxxxxxxxxxxxxxxx
    aaaaaxxxxxxxxxbbbbbbbbb
    三个串的其余部分全部取不一样的一样的字符

    aaaxxxxxxxxxxxxxbbbbbbbbb
    aaaaayyyyyyyyyyyyyyyyyyyyy
    aaaaazzzzzzzzzzzbbbbbbbbb
    显然最后第3个串长度最为紧张
    所以无解的情况就是c-a+b>n,其实就是取得后缀和前缀的长度只和要小于等于n。

    复杂度是O(n)但是这题n是1000。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9'){
    		sum=sum*10+ch-'0';
    		ch=getchar();
    	}
    	return sum*f;
    }
    struct node{
    	int w,id;
    }x[4];
    bool cmp(node a,node b){
    	return a.w<b.w;
    }
    char ans[4][2000];
    int main(){
    	int a=read(),b=read(),c=read();
    	int n=read();
    	int s1=1,s2=2,s3=3;
    	if(a>b){
    		swap(a,b);
    		swap(s1,s3);
    	}
    	if(b>c){
    		swap(b,c);
    		swap(s2,s1);
    	}
    	if(a>b){
    		swap(a,b);
    		swap(s1,s3); 
    	} 
    	if(c-a+b>n){
    		printf("NO");
    		return 0;
    	}
    	for(int i=1;i<=n;i++){
    		if(i<=a)ans[s1][i]='a';
    		else if(i>n-(c-a))ans[s1][i]='b';
    		else ans[s1][i]='x';
    	}
    	for(int i=1;i<=n;i++){
    		if(i<=b)ans[s2][i]='a';
    		else ans[s2][i]='z';
    	}
    	for(int i=1;i<=n;i++){
    		if(i<=b)ans[s3][i]='a';
    		else if(i>n-(c-a))ans[s3][i]='b';
    		else ans[s3][i]='y';
    	} 
    	for(int i=1;i<=3;i++){
    		for(int j=1;j<=n;j++)cout<<ans[i][j];
    		cout<<endl; 
    	}
    	return 0;
    } 
    

    F Just a joke

    讲题人说这个题的代码签到思维不签到。

    设给出的图有n个环,m个连通块。

    每次操作可以减少一个环或者加一个连通块或者减少一个连通块或者减少一个环的同时增加一个连通块。

  • 相关阅读:
    光学字符识别OCR-6 光学识别
    光学字符识别OCR-5 文本切割
    光学字符识别OCR-4
    光学字符识别OCR-3
    leetcode 7 Reverse Integer(水题)
    leetcode 1 Two Sum(查找)
    DFS的简单应用(zoj2110,poj1562)
    Havel-Hakimi定理(握手定理)
    zoj1360/poj1328 Radar Installation(贪心)
    饶毅:做自己尊重的人
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/15078220.html
Copyright © 2011-2022 走看看