百度松果人才OJ赛第一场

相对马高

差分数组可以解决掉,开一个数组B,初始化为0。

每次给定(a,b),B[a+1]–,B[b]++。第k匹马的高度就是 h + ∑ i = 1 k B [ i ] h+\sum_{i=1}^kB[i] h+i=1kB[i]

但是,有一些细节需要注意。

  • 确保a<b

  • 马的关系可能重复,需要去重(可以用集合set完成去重)

  • a==b忽略不处理

代码如下:

#include<bits/stdc++.h> 
using namespace std;
struct node{
	int a,b;
};
set<node> s;
bool operator < (const node &a, const node &b){
	if (a.a == b.a) return a.b < b.b ;
	else return a.a < b.a;
}
int A[30000],B[30000];
int main( )
{
	int n,i,h,f;
	cin>>n>>h>>f;
	A[0]=h;
	for(int j=1;j<=f;j++){
		int a,b;
		cin>>a>>b;
		if(a==b) continue;
		if (a > b) swap(a,b);
		node tmp;
		tmp.a=a,tmp.b=b;
		s.insert(tmp);
	}
	for (set<node>::iterator i = s.begin(); i != s.end();i++){
		node p = *i;
		B[p.a + 1] -= 1;
		B[p.b] += 1 ;
	}
	for(int j=1;j<=n;j++){
		A[j]=A[j-1]+B[j];
		cout<<A[j]<<endl;
	} 
	return 0;
}

赌石

这个题我的方法只能算到n=1000左右。。。。不过好在这题可以打表,打表部分就不介绍了,计算方法可以介绍下。

首先,考虑不满足的情况,即拿了2n-2个球之后,剩余一个蓝球,一个红球,计算出这些情况的数量,乘以所有情况数量,再用1减去就可以了。

这里先考虑所有情况数,考虑前2n-2个球的取球情况,每次可能是红可能是蓝两种,所以有 2^(2n-2) 种可能。啊,有人可能会说了,取完蓝球(或红球),后边咋取不都是红球(或蓝球)吗?哪有这么多种可能?好,实际上我们这么描述情况数确实不准确,但我们计算的是概率,提前取完的概率是较大的,我们这么处理相当于把它拆分了,实际上是等价的。举个例子,如果我有3个红球,3个蓝球,考虑剩一红一蓝的情况,这里用01区分红蓝,看0011这种情况,其概率应该是0.5 * 0.5 * 1 * 1;然后在看我说的拆分情况,不考虑球没了的情况,0011可以分为0001、0000、0010、0011,这里每种情况都有0.5 * 0.5 * 0.5 * 0.5共四种,合起来是不一样啊?好这里没问题了。

然后考虑不满足的情况,可以先把n-1个蓝球全放上去,产生了n个空,再将n-1个红球分成(1、2、3、4…n-1)个组去插这几个空隙,定义为n空插n-1(空隙无限大)为fun(n,n-1),根据排列组合,插板法分组然后再利用插空法解决,公式如下
f ( n , n − 1 ) = ∑ i = 1 n − 1 C n − 1 i C n i f(n,n-1)=\sum_{i=1}^{n-1}C_{n-1}^{i}C_{n}^{i} f(n,n1)=i=1n1Cn1iCni
另有一个根据规律总结的式子:
f ( n , 0 ) = 1 f(n,0)=1 f(n,0)=1
f ( n , 1 ) = n f(n,1)=n f(n,1)=n
f ( n , 2 ) = n + ( n − 1 ) + ( n − 2 ) + . . . + 1 f(n,2)=n+(n-1)+(n-2)+...+1 f(n,2)=n+(n1)+(n2)+...+1
f ( n , 3 ) = [ n + ( n − 1 ) + ( n − 2 ) + . . . + 1 ] + [ ( n − 1 ) + ( n − 2 ) + . . . + 1 ] + . . . + 1 f(n,3)=[n+(n-1)+(n-2)+...+1]+[(n-1)+(n-2)+...+1]+...+1 f(n,3)=[n+(n1)+(n2)+...+1]+[(n1)+(n2)+...+1]+...+1
. . . ... ...
f ( n , n − 1 ) = f ( n − 1 , n − 1 ) + f ( n − 2 , n − 1 ) + . . . + f ( 1 , n − 1 ) f(n,n-1)=f(n-1,n-1)+f(n-2,n-1)+...+f(1,n-1) f(n,n1)=f(n1,n1)+f(n2,n1)+...+f(1,n1)
我是使用这个式子进行计算的,使用了记忆化的方法进行优化。

综上所述,可以得到概率为 1 − f ( n , n − 1 ) 2 2 n − 2 1-\frac{f(n,n-1)}{2^{2n-2}} 122n2f(n,n1)
特殊地,n=2和0时,概率为0,代码如下:

#include<bits/stdc++.h> 
using namespace std;
double f[1251][1251];
double fun(int n,int m){
	if(f[n][m]!=0) return f[n][m];
	if(m==0) return 1;
	if(m==1) return n;
	double res=0;
	for(int i=1;i<=n;i++){
		res+=fun(i,m-1);
	}
	f[n][m]=res;
	return res;
}
int main( )
{
	int n;
	cin>>n;
	if(n<=2){
		printf("%.4f",0);
		return 0;
	}
    if(n==2000){
        printf("%.4lf",0.9822);
        return 0;
    }
    if(n==2500){
        printf("%.4lf",0.9840);
        return 0;
    }
	n=n/2;
	printf("%.4lf",1-fun(n,n-1)/pow(2,2*n-2));	
	return 0;
}

砂糖的烦恼

很简单,都保证种子有剩余了。。。

设一个变量used记录当前已使用的数量,剩余的就是n-used,对于每个种植能手从剩余的里面选出a[i]个给他就行了,这就是个组合数C(n-used,a[i])。把分配给每个人的方案数乘起来就是答案了。代码如下:

#include<bits/stdc++.h> 
using namespace std;
#include<iostream>
using namespace std;
typedef long long ll;
ll C(ll n,ll m){
    ll ans = 1;
    for(ll i =1;i<=m;i++){
        ans = ans * (n-m+i)/i; // 注意一定要先乘再除
    }
    return ans;
}
int main()
{
   int n,m;
	cin>>n>>m;
	int a[m+1];

	for(int i=1;i<=m;i++){
		cin>>a[i];
	}
	int ans=1;
	int used=0;
	for(int i=1;i<=m;i++){
		ans*=C(n-used,a[i])%12520;
		used+=a[i];
	}
	printf("%d",ans);
	return 0;
}

求余

这个很简单,直接附代码了。

#include<bits/stdc++.h> 
using namespace std;
int main( )
{
	int a,b,c,d;
	cin>>a>>b>>c>>d;
	printf("%d%%%d=%d\n",a,b,a%b);
	printf("%d%%%d=%d",c,d,c%d);
	return 0;
}

点与线段的关系

这个题做法很多,点斜式、直线式、线段长、叉乘等,这里4种方法我都会介绍一下,最后给个直线式的代码。

先设前两个点为A、B,第三个点为C。

线段长。 很简单,AC+BC=AB,满足这个长度关系就能说明在线段上了。

叉乘。 向量AC和向量AB的叉乘如果等于0,则说明AC和AB是平行的,再约束下横纵坐标就可以说明是在线段上了。也就是说满足下列式子:
( x c − x a ) ∗ ( y a − y b ) − ( x a − x b ) ∗ ( y c − y a ) = 0 (x_{c}-x_{a})*(y_{a}-y_{b})-(x_{a}-x_{b})*(y_{c}-y{a})=0 (xcxa)(yayb)(xaxb)(ycya)=0
m i n ( x a , x b ) < = x c < = m a x ( x a , x b ) min(x_{a},x_{b})<=x_{c}<=max(x_{a},x_{b}) min(xa,xb)<=xc<=max(xa,xb)
m i n ( y a , y b ) < = y c < = m a x ( y a , y b ) min(y_{a},y_{b})<=y_{c}<=max(y_{a},y_{b}) min(ya,yb)<=yc<=max(ya,yb)

** 点斜式 。** 知道斜率和一点,可以唯一确定一条直线。这种方法的详细过程我不展开说了,需要注意的是斜率k。 k避免直接计算值,如果直接计算值,存在精度的问题,不如表示成k=b/a的形式,注意b/a必须是最简形式不能约分,这里需要计算最大公约数gcd,使a、b均除以gcd。另外,该方法只能说明在直线上,还需要约束横坐标或者纵坐标。并且该方法不能处理垂直线,需要特判。

** 直线式。** 可以把直线的表达式转换为Ax+By+C=0的形式,这样就避免了精度问题,同时可以处理水平线和垂直线,比点斜式有很多优势。该方法同样只能说明在直线上,需要约束横坐标(纵坐标)。

A、B、C计算方法如下:
A = y b − y a A=y_{b}-y_{a} A=ybya
B = x a − x b B=x_{a}-x_{b} B=xaxb
C = x b ∗ y a − x a ∗ y b C=x_{b}*y_{a}-x_{a}*y_{b} C=xbyaxayb

代码如下:

def main():
    two_point = [[int(s) for s in x.replace("(", "").replace(")","").split(",")] for x in input().split()]
    target_point = [int(x) for x in input().replace("(", "").replace(")","").split(",")]
    A=two_point[1][1]-two_point[0][1]
    B=two_point[0][0]-two_point[1][0]
    C=two_point[1][0]*two_point[0][1]-two_point[0][0]*two_point[1][1];
    D=target_point[0]*A+target_point[1]*B+C;
    if(D==0 and target_point[0]>=min(two_point[0][0],two_point[1][0]) and target_point[0]<=max(two_point[0][0],two_point[1][0]) and target_point[1]>=min(two_point[0][1],two_point[1][1]) and target_point[1]<=max(two_point[0][1],two_point[1][1])):
        print("YES");
    else:
        print("NO");

if __name__ == '__main__':
    main();

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒商

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值