题目好难懂~告诉你文章中有多少个单词,只有单词"Yes","No" , yes占三个字节,no是两个字节,共有 a个单词,s个字节
那么yes就是 n=s-2*a ,no就是m=(s-3*a)/2个
将yes看成1,no看成0
形成了01的字符串,每次将可组合成的某个字符串右移一位后,将最左端置位1,然后和原串每一位比较,得到不一样的位的个数,求的是所有的组合方式的平均值
数学公式推一推就出来的说~~~
理解为每一位不同的是多少次(一开始是用期望dp写的,开了三位数组,如果数据范围扩大,推组合数公式不会卡时间)
1有n个,0有m个,那么最高位的时候只有为0的才会不一样: C(n,n+m-1)
其他位置只要跟前面一个位置不一样就可以了,那么就是说当前和前面一个位置一个0一个1 , 共C(n-1 , n+m-2),因为1,0可以交换所以要乘2
之后 所有位置都是满足的:
平均值就是(2*(n+m-1)*C(n-1,n+m-2)+C(n,n+m-1)) / C(n,n+m)
----->(2*n*C(n,n+m-1)+C(n,n+m-1)) / C(n,n+m)
----->(2*n+1)*C(n,n+m-1) /(C(n,n+m-1)+C(n-1,n+m-1))----->(2*n+1)*C(n,n+m-1) /((1+n/m)*C(n,n+m-1))
----->(2*n+1)/((1+n)/m)
----->(2*m*n+m)/(n+m)
先附期望dp的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
int n, s, m1, m2;
double dp[2][N][2];
int main()
{
int t, cas = 1;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &s);
m1 = s - n * 2;
m2 = n * 3 - s;
dp[n % 2][m1][0] = dp[n % 2][m1][1] = 0;
for(int i = n - 1; i >= 0; i--)
{
for(int j = min(m1, i); j >= 0 && i - j <= m2; j--)
{
double p1 = (m1 - j) * 1.0 / (n - i);
double p2 = (m2 - (i - j)) * 1.0 / (n - i);
if(j + 1 <= m1)
{
dp[i % 2][j][0] = dp[(i + 1) % 2][j + 1][0] * p1 + (dp[(i + 1) % 2][j][1] + 1) * p2;
dp[i % 2][j][1] = (dp[(i + 1) % 2][j + 1][0] + 1) * p1 + dp[(i + 1) % 2][j][1] * p2;
}
else
{
dp[i % 2][j][0] = (dp[(i + 1) % 2][j][1] + 1) * p2;
dp[i % 2][j][1] = dp[(i + 1) % 2][j][1] * p2;
}
}
}
printf("Case %d: %.12f\n", cas++, dp[0][0][0]);
}
return 0;
}
然后是组合数的代码,是不是超短
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
int n, s, m1, m2;
int main()
{
int t, cas = 1;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &s);
m1 = s - n * 2;
m2 = n * 3 - s;
double ans = (2.0 * m1 * m2 + m2) / (m1 + m2);
printf("Case %d: %.12f\n", cas++, ans);
}
return 0;
}