2021牛客暑期多校训练营5,签到题BDHJK
Posted 小哈里
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营5,签到题BDHJK相关的知识,希望对你有一定的参考价值。
题号 标题 已通过代码 通过率 团队的状态
A Away from College 点击查看 9/53 未通过
B Boxes 点击查看 637/2596 通过(数学期望,贪心结论)
C Cheating and Stealing 点击查看 88/438 未通过
*D Double Strings 点击查看 368/1325 未通过(二维字符串dp)
E Eert Esiwtib 点击查看 26/116 未通过
F Finding Points 点击查看 72/977 未通过
G Greater Integer, Better LCM 点击查看 107/820 未通过
H Holding Two 点击查看 1453/2707 通过(01矩阵构造)
I Interval Queries 点击查看 31/204 未通过
J Jewels 点击查看 298/3197 未通过(建图,最小匹配权)
K King of Range 点击查看 896/6008 通过(单调队列)
B Boxes
题意:
- 有n个盒子,每个盒子里有一个黑色或白色的球,概率为1/2
- 打开盒子需要对应的成本wi,或者可以使用代价c获得尚未打开的盒子中黑球的数量。
- 求求出所有颜色的球的最低成本的数学期望值是多少
思路:
- 首先 C 最多只用花一次,且可以在最开始花,所以有两种策略:
①直接全部打开,代价:Σwi
②将 wi 升序排序。先花 C 的代价,剩下的就相当于一个随机 01 序列从前往后开,开到一个后缀全是同色的为止。代价:C + Σ wi(1-1/(2^(n-i))) - 两者取较小值即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
double w[maxn];
int main(){
int n; double c; cin>>n>>c;
double sum=0, ans=0;
for(int i = 1; i <= n; i++)
cin>>w[i], sum+=w[i];
sort(w+1,w+n+1);
for(int i = 1; i <= n; i++)
ans = ans/2+w[i];
printf("%.10lf\\n", sum-max(ans-c,0.0));
return 0;
}
H Holding Two
题意:
- 构造一个n*m的01矩阵,满足没有同一行同一列同一对角线上存在三个连续的0或者1.
思路:
- 可以直接按照样例构造成两个0两个1连续的形式
00110011…
11001100…
00110011…
11001100…
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, m; cin>>n>>m;
string s="", t=""; int ok=1;
for(int i = 1; i <= m; i+=2){
if(ok)s += "11", t+="00";
else s+="00", t+="11";
ok = !ok;
}
s=s.substr(0,m); t=t.substr(0,m);
for(int i = 1; i <= n; i++){
if(i%2==1)cout<<s<<"\\n";
else cout<<t<<"\\n";
}
return 0;
}
K King of Range
题意:
- 给出一个长为n的序列,m次操作,每次操作求对于一个指定的k,原序列存在多少个连续子序列,满足该区间内最大值减最小值严格大于k。
思路:
- 令 Ri 为 l=i 时,满足极差大于 k 的最小的 r,如果不存在则记 Ri = n+1。那么显然有 R1 ≤ R2 ≤ … ≤ Rn。
- 首先可以用 Set 之类的 STL 工具来快速维护极差,所以可以在均摊 O(log n) 的时间求 Ri。
- 一个线性的做法:注意到因为区间端点都是单调的,所以可以维护两个单调队列,其中一个递增序列,队首维护最小值,一个递减序列,队首维护最大值,每次弹出两个队列中队首靠前的一个,直到极差 ≤ k。那么就可以在均摊 O(1) 的时间内求 Ri 了。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
LL a[maxn];
int main(){
LL n, m; cin>>n>>m;
for(int i = 1; i <= n; i++)cin>>a[i];
while(m--){
LL k; cin>>k;
deque<LL>ma, mi; //维护区间[l,i]的最大值和最小值,按顺序入队
LL ans = 0, l = 1; //ans维护极差≤k的区间个数
for(LL i = 1; i <= n; i++){
while(ma.size()&&a[ma.back()]<a[i])ma.pop_back();//递减序列,队首维护最大值
ma.push_back(i);
while(mi.size()&&a[mi.back()]>a[i])mi.pop_back();//递增序列,队首维护最小值
mi.push_back(i);
while(mi.size()&&ma.size()&&a[ma.front()]-a[mi.front()]>k){
if(ma.front()==l)ma.pop_front();
if(mi.front()==l)mi.pop_front();
l++;
}
ans += i-l+1;//如果a[l..i]满足条件,其(i-l+1)个子数组都满足条件
}
cout<<n*(n+1)/2-ans<<"\\n";
}
return 0;
}
J Jewels
题意:
- 给出n个点的位置x,y,z以及速度v,从第t=0秒开始打捞,打捞点i的代价为x^2+y^2+(z+t*v)^2,求打捞所有点的最小代价。
思路:
- 所有的宝石肯定都在 0~n-1 这 n 个时刻被挖掉,那么问题就变成一个最小权匹配了,一边是时刻,一边是宝石,边权就是在这个时刻挖这个宝石所消耗的体力值。
- 最小匹配权,可以用KM(即匈牙利算法),也可以跑费用流。但是神仙们直接乱搞,,反正就是暴力找增广路找到不能更优为止,这样的代码是最短的。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n, ans;
struct jew{LL z, v, id;}a[310];
LL f(LL t, jew x){t--;return t*t*x.v*x.v+2*t*x.v*x.z;}//边权
void g(int x){
int t;
for(int i = 1; i <= n; i++){ //找到当前宝石所匹配的时间
if(a[i].id==x){t=i; break;}
}
for(int i = 1; i <= n; i++){//枚举所有的边
if(f(i,a[i])+f(t,a[t])>f(t,a[i])+f(i,a[t])){//找增广路
swap(a[i],a[t]); //匹配宝石到时间更优的位置
g(a[t].id); g(x); //暴力递归找增广路
}
}
}
int main(){
cin>>n;
for(int i = 1; i <= n; i++){
LL x, y, z, v; cin>>x>>y>>z>>v;
ans += x*x+y*y+z*z;
a[i] = (jew){z,v,i};
}
for(int i = 1; i <= n; i++)g(i);
for(int i = 1; i <= n; i++)ans+=f(i,a[i]);//累加答案
cout<<ans<<"\\n";
return 0;
}
D Double Strings
题意:
- 给出两个字符串A, B, 在其中选出两个等长的子序列(可以不连续)a, b,满足a的字典序严格小于b,求这样的方案有多少个,答案mod(1e9+7)
思路:
- 好的方案的构成是“一段相同的前缀+一个不同字符(a比b小)+长度相同的任意后缀”。枚举不同的字符在两个序列中的位置。
- 用dp[i][j]表示只考虑 A 中的前 i 个字符和 B 中的前 j 个字符时的相同的子序列的个数,转移可以 O(1),这样可以统计出相同的前缀个数,这部分是 O(|s|*|t|) 的。
- 长度相同的任意后缀也可以用类似的 dp 计算,或者设 A 中此时剩余长度为 x, B 中剩余长度为 y,不失一般性地设 x≤y,现在要求的就是 ΣC(x,i)*C(y,i) = ΣC(x,x-i)*C(y,i) = C(x+y,x),这部分也是 O(|s|*|t|) 的。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
int n, m, slen, tlen;
char s[5010], t[5010];
int dp[5010][5010], f[5010][5010];
int main(){
cin>>s+1>>t+1;
slen=strlen(s+1), tlen=strlen(t+1);
for(int i=1; i <= tlen; i++){
for(int j=1; j <= slen; j++){
dp[i][j]= (dp[i][j]+(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mod)%mod)%mod;
if(t[i]==s[j])dp[i][j]=(dp[i][j]+1LL+dp[i-1][j-1])%mod;
f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
if(t[i]>s[j])f[i][j] = (f[i][j]+1LL+dp[i-1][j-1])%mod;
}
}
cout<<f[tlen][slen]<<"\\n";
return 0;
}
以上是关于2021牛客暑期多校训练营5,签到题BDHJK的主要内容,如果未能解决你的问题,请参考以下文章