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的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营1, 签到题DFBG

2021牛客暑期多校训练营2,签到题CDFKI

“蔚来杯“2022牛客暑期多校训练营5,签到题KBGHFCD

2021牛客暑期多校训练营7,签到题FHI

2021牛客暑期多校训练营6,签到题CFHI

2021牛客暑期多校训练营10,签到题FH