前段时间的考试试题

有趣的序列

洛谷链接
这一道题是一道卡特兰数的题,但是因为数据量以及要进行模运算的原因,要采用我先前博客中写到的通项式。当然因为不可以直接算阶乘,因此采取分解阶乘的方法转换成乘法。

Code

#include<bits/stdc++.h>
#define maxn 1000002
#define Int64 long long
using namespace std;
int primes[2*maxn];
int sum[2*maxn];
bool isp[2*maxn];
int newp=0;
void Eular_Sieve(){
	for(int i=2;i<2*maxn;i++){
		if(!isp[i]){
			primes[++newp]=i;
		}
		for(int j=1;j<=newp&&primes[j]*i<2*maxn;j++){
			isp[primes[j]*i]=true;
			if(!(i%primes[j]))break;
		}
	}
	return;
}
Int64 qkpow(int a,int b,int p){
	//a的b次方mod p 
	if(b==1)return a;
	if(b%2){
		Int64 t=qkpow(a,b/2,p);
		t=t*t%p;
		t=t*a%p;
		return t;
	}
	else {
		Int64 t=qkpow(a,b/2,p);
		t=t*t%p;
		return t;
	}
}
int n,p;
int main(){
	
	scanf("%d%d",&n,&p);
	Eular_Sieve();
	for(int i=1;i<=newp;i++){
		Int64 t=primes[i];
		Int64 cnt1=0,cnt2=0,cnt3=0;
		while(t<=2*n){
			cnt1+=(2*n/t);cnt2+=((n+1)/t);cnt3+=(n/t);
			t*=primes[i];
		}
		sum[i]=cnt1-cnt2-cnt3;
	}
	Int64 ans=1;
	for(int i=1;i<=newp;i++){
		if(sum[i]){
			(ans*=qkpow(primes[i],sum[i],p))%=p;
		}
	}
	printf("%lld",ans);
	return 0;
	
}

打气球

这道题我就不给网址了,yzoj上面最后一页找

题目描述

Descrition

周末何老板到磁器口游玩。街边有小贩在组织一种打气球游戏,何老板很感兴趣。

店家立了一块布,布上画了N*N的方格,有的方格里挂上了气球,有的没有。

游戏规则如下:

第1步.观察。如果每一行都至少有一个方格没有气球,并且每一列都至少有一个方格没有气球,游戏结束。否则进行第2步。

第2步.抛骰子。店家拿出一个特制的骰子,该骰子有N个面,上面依次有1到N这N 个数字。玩家先后抛两次骰子,设第一次抛出的数字为x,设第二次抛出的数字为y (注:抛出的数字是随机的)。

第3步.打气球。若坐标为(x,y)的格子里有气球,玩家必须将其打爆。子弹1块钱一发。

如果该格子没有气球,忽略该格子,玩家不用开枪,但玩家也需要支付给店家1块钱。

第4步.继续。执行第1步。

何老板是个神枪手,他能做到百发百中。他想你帮他算算,对于当前给出的这局游戏,预计要花多少钱才能结束。

Input

第一行,两个整数N和M,N表示方格的尺寸,M表示游戏开始时,有M个格子里是没有气球的。 接下来M行,每行两个整数x,y,表示坐标为x,y的格子里没有气球。

Output

一行,一个实数,完成游戏预计花费,保留2个小数位。

Sample Input

5 2 
2 3 
4 1

Sample Output

11.77

如果你想看更多的样例输出,可以去WYX大佬的博客

分析

这一道题啊,实际上啊,就是一道期望的题,如果你点开了上面的那一个链接,多半也看见了YB说的话了吧,那么接下来要做的事情就是找出递归的关系
首先,我们都知道每一个格子概率为1/(n*n)
我们设有r行已经打掉,c列已经打掉,函数为f[r][c]=......
那么分类讨论
1.如果说没有抽到有气球的地方,那么就是他自己乘上没有气球的地方的概率
2.如果说打掉的地方只能消除行,那么就是乘上f[r-1][c]以及这些地方的概率,只能消除列同理
3.如果可以同时消去的话,那么就可以类比上面的,写出式子来。
4.当然最后你应该加上1,因为每打一次收费1

式子和化简如下

[f[r][c]=\\ frac{f[r-1][c] imes r(n-c)+f[r][c-1] imes c(n-r)+f[r-1][c-1] imes r imes c+f[r][c] imes(n-r)(n-c)+n^2}{n^2(n-r)(n-c)} ]

好了,既然已经知道了式子,那么就可以比较容易的做出来了,至于边界,那你也可以自己推出来,你可以选择两种方法,dfs or dp
我的代码里面把dfs的注释掉了

Code

#include<bits/stdc++.h>
#define maxn 2003
using namespace std;
double f[maxn][maxn];
bool row[maxn],col[maxn];//行 列 
int n,m;
double dfs(int r,int c){
	double ans=0;
	if(!r&&!c)return 0;
	if(f[r][c]!=-1)return f[r][c];
	if(r)ans+=dfs(r-1,c)*r*(n-c);
	if(c)ans+=dfs(r,c-1)*c*(n-r);
	if(r&&c)ans+=dfs(r-1,c-1)*r*c;
	return f[r][c]=(double)(ans+n*n)/(n*(c+r)-c*r);
}
int main(){
	
	scanf("%d%d",&n,&m);
	int r=n,c=n;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		if(!row[x])r--,row[x]=true;
		if(!col[y])c--,col[y]=true;
	}
	/*for(int i=0;i<=r;i++){
		for(int j=0;j<=c;j++)f[i][j]=-1;
	}
	dfs(r,c);*/
	for(int i=1;i<=n;i++){
		f[i][0]=f[i-1][0]+n/(i+0.0);
		f[0][i]=f[0][i-1]+n/(i+0.0);
	}
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			double tmp=f[i-1][j]*i*(n-j)+f[i][j-1]*(n-i)*j+f[i-1][j-1]*i*j+n*n;
			f[i][j]=tmp/(n*(i+j)-i*j);
		}
	}
	printf("%.2lf",f[r][c]);
	return 0;
}

还有一道题,不过比较水,居然用暴力枚举就可以做出来了,早知道我当时就直接枚举了,题目与韩信点兵类似

原文地址:https://www.cnblogs.com/perisino/p/10384749.html