AtCoder Beginner Contest 194 A~E题解

A - I Scream

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main() 
{
	Angel_Dust;
	int A,B;cin >> A >> B;
	A += B;
	if(A >= 15 && B >= 8)	return cout << "1\n",0;
	else if(A >= 10 && B >=3)	return cout << "2\n",0;
	else if(A >= 3)	return cout << "3\n",0;
	else cout << "4\n";
	return 0;
}

B - Job Assignment

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1005;
int a[N],b[N];

int main() 
{
	Angel_Dust;
	int n;cin >> n;
	forn(i,1,n)	cin >> a[i] >> b[i];
	
	int res = a[1] + b[1];
	forn(i,1,n)	res = min(res,a[i] + b[i]);
	forn(i,1,n)	forn(j,1,n) if(i != j)	res = min(res,max(a[i],b[j]));
	printf("%d\n",res);
	return 0;
}

C - Squared Error

经典套路:拆平方和变成\(\sum\limits_{i = 2}^{n}\sum\limits_{j = 1}^{i - 1}(A_i ^ 2 + A_j^2 - 2 * (A_i + A_j))\)再拆和式分别维护前缀和/前缀平方和即可计算。

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 3e5+7;
ll sum[N],ssum[N],a[N];

int main() 
{
	Angel_Dust;
	int n;cin >> n;
	forn(i,1,n)	cin >> a[i];
	forn(i,1,n)	sum[i] = sum[i - 1] + a[i],ssum[i] = ssum[i - 1] + a[i] * a[i];
	
	ll res = 0;
	forn(i,2,n)
	{
		res += (i - 1) * (a[i] * a[i]);
		res += ssum[i - 1];
		res -= 2 * a[i] * sum[i - 1];
	}
	printf("%lld\n",res);
	return 0;
}

D - Journey

直接求期望是比较困难的。因为按普通套路来看的话这个题如果做dp转移的话大概写成:\(f[x]\)表示使\([1,x]\)中所有点联通的期望,但是这个状态转移不了,因为你根本不能从维度信息得到除了\([1,x]\)之外的点是否是联通的。考虑把问题本身降维一下:问题等价于每次在\([1,n]\)之内抽一个数,独立且等概率,求每个数至少出现一个的期望抽取次数。本身期望是不太好求的,但是可以拆解一下问题:设\(f[x]\)表示当前已经抽到了\(x - 1\)种数,再抽一个不同的数的期望。那么对于:抽到了\(x\)种数再抽一种不同这个事件来说,他的概率是:\((n - x + 1) / n\)。符合几何分布,期望是概率的倒数。接下来根据题目的定义:\(f[1]\)应该是\(0\)特殊处理。对于答案:\(ans = \sum f[i]\)。求和统计即可。

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main() 
{
	int n;cin >> n;
	double res = 0;
	forn(i,1,n - 1)	res += double(n) / i;
	printf("%.18lf\n",res);
	return 0;
}

E - Mex Min

区间求mex是个比较吊比的问题,不太合理。这个题的一个特点就是每次移动区间实际上只会删除一个元素以及增加一个元素。先暴力求出第一个区间的答案,后续考虑移动区间的同时维护答案。每次暴力推mex肯定不可接受,那么方向显然就是想mex怎么移动:假设删除元素产生了一个空的点,这个点如果位置比上一个答案更左,那么显然这个空的位置将会是新的mex。反过来如果没有出现新的空位,或者空位的位置大于上次mex的位置,这时就不能直接说答案会是某个点了。

我的代码直接选择暴力推mex,虽然过了但是我觉得复杂度是假的,之后会对这个问题补上。

UPD:else while的一行可以删掉。因为题目求的是所有区间里面mex的最小值,所以如果答案不会变的更小,那么更新mex是毫无意义的,我们只关心会变得更小的可能性。就没有复杂度一类的问题了。

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 2e6+7;
int a[N],cnt[N];

int main() 
{
	int n,m;scanf("%d%d",&n,&m);
	forn(i,1,n)	scanf("%d",&a[i]);
	int mex = 0,res = 0;
	forn(i,1,m)	++cnt[a[i]];
	while(cnt[mex])	++mex;
	res = mex;
	
	forn(i,1,n - m)
	{
		--cnt[a[i]];
		++cnt[a[i + m]];
		if(cnt[a[i]] == 0 && mex > a[i])	mex = a[i];
		else	while(cnt[mex])	++mex;
		res = min(res,mex);
	}
	printf("%d\n",res);
	return 0;
}
posted @ 2021-03-06 21:51  随处可见的阿宅  阅读(326)  评论(12编辑  收藏  举报