B.Boxes
题意:
给
n
n
n个盒子,每个盒子里只有一个球且黑色或白色,打开盒子的需要价值为
w
i
w_i
wi,有一查看球数(得知当前多少个白球和黑球)的操作,需要价值为
c
c
c.
问:我要得知所有盒子的球的颜色,所需要花费的期望是多少。
思路:
题目的盒子我们考虑一串长度为01的
s
s
s串(0:白球 1:黑球)
令
f
(
i
)
f(i)
f(i)为在第i个以后(包括i)全是0或全是1,的概率.
那么前i-1个可以是任意是排序。有人就会问到那我
s
i
−
1
s_{i-1}
si−1是
0
0
0,
s
i
s_i
si后面也全是
0
0
0那不是会重复的计算嘛。hmm 换种想法思考,
s
i
−
1
s_{i-1}
si−1是
0
0
0,那我后面就全是
1
1
1咯,同理
s
i
−
1
s_{i-1}
si−1是
1
1
1,那我后面就全是
0
0
0咯.所以
f
(
i
)
f(i)
f(i)的总可能数是
2
i
−
1
2^{i-1}
2i−1.
所以这种情况概率为:
f
(
i
)
=
2
i
−
1
2
n
=
1
2
n
−
i
+
1
(
i
∈
[
1
,
n
]
)
\large f(i)=\frac{2^{i-1}}{2^n}= \frac{1}{2^{n-i+1}}(i\in[1,n])
f(i)=2n2i−1=2n−i+11(i∈[1,n])
那么期望值
h
o
p
e
hope
hope就是:
h
o
p
e
=
∑
i
=
1
n
(
s
u
m
[
i
]
∗
f
(
i
)
)
hope=\sum^{n}_{i=1}(sum[i]*f(i))
hope=∑i=1n(sum[i]∗f(i)). (ps:
s
u
m
[
i
]
sum[i]
sum[i] 为前i个盒子的价值和)
当然我们我们开箱肯定希望先开价值小,再开价值大的,盒子价值排个序。
c
c
c操作很特殊。这一步操作后,得知了白球,黑球的数量。
但什么情况可以不操作c呢? 即是
h
o
p
e
>
s
u
m
[
n
]
hope>sum[n]
hope>sum[n]时候没必要知道白黑数,我直接全开完。
代码如下:
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll n,m;
double w[N];
double sum[N];
double c;
void solve(){
n=read();
cin>>c;
rep(i,1,n){
cin>>w[i];
// sum[i]=sum[i-1]+w[i];
}
sort(w+1,w+1+n);
rep(i,1,n){
sum[i]=sum[i-1]+w[i];
}
double ans=0;
double bit=1;
for(int i=n;i>=1;i--){
// 从后往前,便于叠加(2^{n-i+1})
bit/=2;
ans+=sum[i-1]*bit;
}
if(ans+c<=sum[n]){
printf("%.6lf\n",ans+c);
}else{
printf("%.6lf\n",sum[n]);
}
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
solve();
return 0;
}
D.Double Strings
题意
给两个串
s
1
,
s
2
s1,s2
s1,s2,从两个串中各自选出长度相等子序列st1,st2,
问满足 "一段相同的前缀+一个不同字符(a<b)+长度相同的任意后缀"的数目。
思路
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示
s
1
前
i
个
,
s
2
前
j
个
中
共
有
多
少
个
相
同
的
子
序
列
数
目
s1前i个,s2前j个中共有多少个相同的子序列数目
s1前i个,s2前j个中共有多少个相同的子序列数目
如果
s
1
[
i
]
<
s
2
[
j
]
s1[i]<s2[j]
s1[i]<s2[j]那么
d
p
[
i
−
1
]
[
j
−
1
]
∗
C
(
l
e
n
1
−
i
+
l
e
n
2
−
j
,
l
e
n
1
−
i
)
dp[i-1][j-1]*C(len1-i+len2-j,len1-i)
dp[i−1][j−1]∗C(len1−i+len2−j,len1−i)
长度相同的任意后缀怎么得来的呢:
X
=
l
e
n
1
−
i
Y
=
l
e
n
2
−
j
,
不
失
一
般
性
设
X
<
Y
X=len1-i Y=len2-j,不失一般性设X<Y
X=len1−iY=len2−j,不失一般性设X<Y
得到:
∑
i
=
0
X
(
C
(
X
,
i
)
∗
(
Y
,
i
)
)
=
∑
i
=
0
X
(
C
(
X
,
X
−
i
)
∗
(
Y
,
i
)
)
=
C
(
X
+
Y
,
X
)
\sum^{X}_{i=0}(C(X,i)*(Y,i))=\sum^{X}_{i=0}(C(X,X-i)*(Y,i))=C(X+Y,X)
∑i=0X(C(X,i)∗(Y,i))=∑i=0X(C(X,X−i)∗(Y,i))=C(X+Y,X)
证明:
代码如下:
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
const ll MOD=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
char s1[5010],s2[5010];
ll bit[5010];
void init(){
bit[0]=1;
rep(i,1,5000){
bit[i]=(bit[i-1]<<1)%P;
}
return ;
}
int Fac[10010],Inv[10010];
void Prepare(int n)
{
Fac[0] = 1;
for (int i = 1; i <= n; i ++)
Fac[i] = 1LL * Fac[i - 1] * i % MOD;
Inv[0] = Inv[1] = 1;
for (int i = 2; i <= n; i ++)
Inv[i] = MOD - 1LL * (MOD / i) * Inv[MOD % i] % MOD;
for (int i = 2; i <= n; i ++)
Inv[i] = 1LL * Inv[i - 1] * Inv[i] % MOD;
}
inline int C(int u, int v)
{
if (u < 0 || v < 0 || u < v)
return 0;
return 1LL * Fac[u] * Inv[v] % MOD * Inv[u - v] % MOD;
}
ll dp[5050][5010];
void solve(){
// cout<<C(2,5)<<endl;
scanf("%s%s",s1+1,s2+1);
init();
int len1=strlen(s1+1);
int len2=strlen(s2+1);
Prepare(len1+len2);
ll ans=0;
rep(i,0,len1){
dp[i][0]=1;
}
rep(j,1,len2){
dp[0][j]=1;
}
rep(i,1,len1){
rep(j,1,len2){
// 这里存在减法操作可能得到为负数 加个P
dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+P)%P;
if(s1[i]==s2[j]){
dp[i][j]=(dp[i][j]+dp[i-1][j-1])%P;
}else if(s1[i]<s2[j]){
int x=len1-i;
int y=len2-j;
if(x>y) swap(x,y);
// printf("i:%d j:%d\n",i,j);
// printf("x:%d y:%d C:%d \n",x,y,C(x+y,x));
// printf("dp:%d\n",dp[i-1][j-1]);
ans=(ans+dp[i-1][j-1]*C(x+y,x)%MOD)%MOD;
}
}
}
// rep(i,1,len1){
// rep(j,1,len2){
// printf("%d ",dp[i][j]);
// }
// printf("\n");
// }
printf("%lld\n",ans);
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
solve();
return 0;
}
K.King of Range
题意:
给一数组,问有多少个区间的极差值 > k >k >k
思路:
对于区间
[
l
,
r
]
[l,r]
[l,r],我们可以先固定右端点
r
r
r,寻找离
r
r
r最近的
l
l
l且满足区间
[
l
,
r
]
[l,r]
[l,r]极差值
>
k
>k
>k.
于是我们用到了两个队列,分别是递增队列和递减队列。
队列存放
a
a
a数组的下标。
关于递增和递减队列,都有左右端点。两个队列的左端点必定是极差最大值。开始移动两个队列的左端点。
计算两个左端点 对应
a
a
a数组的极差值,大于
k
k
k 开始移动。该值不大于
k
k
k,那后面的所有元素极差值都不会满足,终止。
代码如下(详解)
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll n,m;
ll a[N];
int qadd[N],qde[N]; // 递增队列 递减队列 都是存放a数组的下标
void solve(){
n=read();
m=read();
rep(i,1,n){
scanf("%d",a+i);
}
while(m--){
int k=read();
int lde=1,rde=1; // 递减队列 lde:左端点 rde:右端点
int ladd=1,radd=1; // 递增队列 ladd:左端点 radd:右端点
qde[1]=1;qadd[1]=1; // 初始化
ll ans=0;
int cnt=0;
for(int i=2;i<=n;i++){
//首先新进来的a[i] 需添加到两队列
for(;lde<=rde and a[qde[rde]]<=a[i];rde--) ;
qde[++rde] =i;
for(;ladd<=radd and a[qadd[radd]] >= a[i];radd--);
qadd[++radd]=i;
//两个队列的左端点开始移动 知道两端点对应的a数组极差值<=k 就结束或者不能移动啦。
while(lde<=rde and ladd<=radd and a[qde[lde]]-a[qadd[ladd]]>k){
// 队列存放是a数组下标,固定r,我们肯定是寻求离r最近的l
// 判断
if(qde[lde]<qadd[ladd]){
// qde[led]为左端点
cnt=qde[lde];
lde++; //队列左端点移动
}else{
//qadd[ladd]为左端点 ,另个为右端点
cnt=qadd[ladd];
ladd++;
}
}
// printf("i:%d cnt:%d\n",i,cnt);
ans+=cnt;
}
cout<<ans<<endl;
}
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out1.txt","w",stdout);
solve();
return 0;
}