牛客第五场 题解

比赛传送门
作者: fn


基本题

H题 Holding Two 拿两个

题目大意
求一个没有三连的01矩阵

考察内容
构造,思维

分析
构造形如

00110011...
11001100...
00110011...
11001100...
........

的矩阵即可

#include<bits/stdc++.h> // H 
#define ll long long
using namespace std;
const int N=1010;
ll n,m;
char s[N][N];
int main(){ 
	for(int i=1;i<=1000;i++){
		for(int j=1;j<=1000;j++){
			if(i%4==1 || i%4==2){
				if(j%2==1){
					s[i][j]='1';
				}
				else{
					s[i][j]='0';
				}
			}
			else{
				if(j%2==1){
					s[i][j]='0';
				}
				else{
					s[i][j]='1';
				}
			}
		}
	}
	cin>>n>>m;
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cout<<s[i][j];
		}
		puts("");
	}
	return 0;
}

进阶题

B题 Boxes 盒子

题目大意
给定 n n n 个装有黑球或白球的盒子,可以使用 C C C 的花费知道剩下所有球中黑球的个数

考察内容
贪心,概率与数学期望

分析
由贪心策略,要么在最开始花一次 C C C (这样之后一直可以得到 “剩下所有球中黑球的个数” 这个信息),要么一直不花,直接全部打开。

这两种策略的数学期望如下:

  1. 直接全部打开,代价: ∑ i = 1 n w i \sum\limits_{i=1}^{n}{w_{i}} i=1nwi

  2. w i w_{i} wi 升序排序。先花 C C C 的代价,剩下的就相当于一个随机 01 序列从前往后开,开到一个后缀全是同色的为止。代价: C + ∑ i = 1 n − 1 w i ( 1 − 1 2 n − i ) C +\sum\limits_{i=1}^{n-1}{w_{i} (1-\frac{1}{2^{n-i}})} C+i=1n1wi(12ni1)

两者取较小值即可。

#include<bits/stdc++.h>
using namespace std;
int n;
double C,w[110001],s,f=1;
int main()
{
	scanf("%d%lf",&n,&C);
	for(int i=1;i<=n;i++)scanf("%lf",w+i),s+=w[i];
	sort(w+1,w+1+n);
	double ans=C;
	for(int i=n;i;i--)ans+=(1-f)*w[i],f/=2;
	printf("%f\n",min(ans,s));
}

K题 King of Range 范围之王

题目大意
给定 n n n 个整数, m m m 次询问,每次询问输出极差大于 k k k 的区间个数。

考察内容
单调队列

分析
本问题可以转化为,对每个左端点 l i l_{i} li ,求解满足极差大于 k k k 的最小的 r i r_{i} ri , 那么可以得到 r 1 r_{1} r1 r 2 r_{2} r2 ≤ … ≤ r n r_{n} rn

首先考虑用 multiset 来维护极差,可以在均摊 O ( l o g n ) O(log n) O(logn) 的时间求 r i r_{i} ri,但是交上去tle了。T-T

线性的做法:
注意到因为区间端点都是单调的,所以可以维护两个单调队列,其中一个递增序列,队首维护最小值,一个递减序列,队首维护最大值,每次弹出两个队列中队首靠前的一个,直到极差 ≤ k ≤k k 。那么就可以在均摊 O ( 1 ) O(1) O(1) 的时间内求 r i r_{i} ri 了。

#include<bits/stdc++.h>
using namespace std;
const int N=100100;
long long ans;

int n,m,a[N],q1[N],q2[N]; // q1,q2为单调队列 

int main(){
	ios::sync_with_stdio(0); cin.tie(0);
	
	cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i];
	
	while(m--){
		int k; cin>>k;
		ans=0;
		int t1=1,t2=1,w1=0,w2=0;
		q1[t1]=q2[t2]=0;
		for(int i=1,j=0;i<=n;++i){
			for(;j<=n && a[q1[t1]]-a[q2[t2]]<=k;){
				++j;
				if(j>n)break;
				for(;t1<=w1 && a[q1[w1]]<=a[j];--w1);
				q1[++w1]=j;
				for(;t2<=w2 && a[q2[w2]]>=a[j];--w2);
				q2[++w2]=j;
			}
			ans += n+1-j;
			if(q1[t1]==i)++t1;
			if(q2[t2]==i)++t2;
		}
		cout<<ans<<endl;
	}
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值