• 周二. 9月 27th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

2021牛客暑期多校训练营4

admin

11月 28, 2021

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个连通块。

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

发表回复

您的电子邮箱地址不会被公开。