赞
踩
差分数组可以解决掉,开一个数组B,初始化为0。
每次给定(a,b),B[a+1]–,B[b]++。第k匹马的高度就是 h + ∑ i = 1 k B [ i ] h+\sum_{i=1}^kB[i] h+i=1∑kB[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,n−1)=i=1∑n−1Cn−1iCni
另有一个根据规律总结的式子:
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+(n−1)+(n−2)+...+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+(n−1)+(n−2)+...+1]+[(n−1)+(n−2)+...+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,n−1)=f(n−1,n−1)+f(n−2,n−1)+...+f(1,n−1)
我是使用这个式子进行计算的,使用了记忆化的方法进行优化。
综上所述,可以得到概率为
1
−
f
(
n
,
n
−
1
)
2
2
n
−
2
1-\frac{f(n,n-1)}{2^{2n-2}}
1−22n−2f(n,n−1)
特殊地,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
(xc−xa)∗(ya−yb)−(xa−xb)∗(yc−ya)=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=yb−ya
B
=
x
a
−
x
b
B=x_{a}-x_{b}
B=xa−xb
C
=
x
b
∗
y
a
−
x
a
∗
y
b
C=x_{b}*y_{a}-x_{a}*y_{b}
C=xb∗ya−xa∗yb
代码如下:
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();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。