ABC163(Atcoder beginner contest 163)部分题解

题解

A

思路: 水题,π保留7位小数乘法就可以啦。

话说不会有人不知道π≈3.1415926吧

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

double n,p=3.14159265358979;

signed main()
{
	cin>>n;
	cout<<fixed<<setprecision(15)<<2*n*p<<endl;
	
	return 0;
}

B

思路: 直接模拟即可。

如果总天数小于等于假期的天数,就输出两者的差;否则输出-1。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

int d,n,tmp;

signed main()
{
	cin>>d>>n;
	for (int i=1;i<=n;i++)
	{
		cin>>tmp;
		d-=tmp;
	}
	if (d>=0)  cout<<d<<endl;
	else cout<<-1<<endl;
}

C

思路: 统计每人作为上司的次数即可。

前三题有多水后两题就有多难

思路:

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,tmp;
int visited[200005];

signed main()
{
	cin>>n;
	for (int i=2;i<=n;i++)
	{
		cin>>tmp;
		visited[tmp]++;
	}
	for (int i=1;i<=n;i++)  cout<<visited[i]<<endl;
	cout<<endl;
	
	return 0;
}

D

思路:

看数据范围: N 2 × 1 0 5 N≤2×10^5 1 K N + 1 1≤K≤N+1

很明显,高桥君想让我们搞出一个O(n)的算法而不是O(1)的算法。

所以,我们枚举选的数的数量i,每次尝试在O(1)时间内算出取 i i 个数的不同和的数量。

为了方便,我们需要去掉前面的那个 1 0 100 10^{100} ;同时为了防止影响答案的正确性,我们设定x个数的和与y个数的和一定不同(x≠y),这样每次算 取 i i 个数的不同和的数量 就独立了。

好的,一切都处理完了,我们开始思考怎么O(1)时间算它。

即,所有能够取到的和一定是连续的(很明显啊,读者自行思考 ),而一个连续的闭区间 [ l , r ] [l,r] 的数的数量为 r l + 1 r-l+1 ,所以我们只需要求出 l l r r 的值。易得:

r = Σ j = n i + 1 n j r=Σ_{j=n-i+1}^n j
l = Σ j = 0 i 1 j l=Σ_{j=0}^{i-1} j

注意这里的等差数列求和可以用公式,即 Σ l r i = ( l + r ) × ( r l + 1 ) / 2 Σ_l^r i=(l+r)×(r-l+1)/2

综上所述,答案就是: Σ i = k n + 1 r l + 1 Σ_{i=k}^{n+1} r-l+1

时间复杂度 O ( n k ) O(n-k)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;

int n,k,ans=0;

int get_sum(int l,int r)
{
	return (((l+r)*(r-l+1))/2ll)%mod;
}

signed main()
{
	cin>>n>>k;
	for (int i=k;i<=n+1;i++)  ans=(ans+((get_sum(n-i+1,n)-get_sum(0,i-1)+1)%mod+mod)%mod)%mod;
	cout<<ans%mod<<endl;
	
	return 0;
}

花絮

本题在我们班的花絮极多:

①我: 切掉
②大佬A: 压到两行再提交!
③大佬B: 哇哇哇,这么难? (10秒后)哦哦哦,这么简单!
④大佬C: 一边做题一边唱歌,连续多次WA后自闭。

代码

E


前言

考试的时候构造了一个矩阵,其中第 i i 列的第 j j 行表示第 i i 个小屁孩到第 j j 个位置的幸福度。

那么,我们就需要取 n n 个数,其中每行仅取一个数且每列也仅取一个数,然后答案就是这些数的综合的最大值

于是开始想dp,在两次WA,一次RE,一次MLE以及两次TLE中自闭……


思路(正解)

赛后诸葛亮

考虑贪心。

我们应该发现,我们应该让积极性更高的小屁孩先选择位置。那么,他/她 一定会站在目前唯一的未站小屁孩的区间的左端点或右端点。由于每次小屁孩都站在端点处,所以未被选择的区间有且仅有一个

于是这就成为了一个不能再明显的区间动规。状态设计 d p l , r dp_{l,r} ,表示目前唯一未站小屁孩的区间为 [ l , r ] [l,r] d p l , r dp_{l,r} 存下了目前得到了最大幸福值

然后推一下状态转移。注意这里要分类讨论:

编号为 i i 的小屁孩跑到左端点去了! 他导致现在的区间成为了 [ l + 1 , r ] [l+1,r] ,且他贡献了 a i × i l a_i×|i-l| 的幸福值;
编号为 i i 的小屁孩跑到右端点去了! 他导致现在的区间成为了 [ l , r 1 ] [l,r-1] ,且他贡献了 a i × i r a_i×|i-r| 的幸福值。

后记

思路出来后,我开始打 d p dp ,结果死得一批得惨,连调都调不出来(还不是我太弱)。

于是,本蒟蒻臭不要脸地看了另外一篇题解,启发我打了记忆化搜索以防止调试时间过长(因为动规 d p dp 需要逆推,而记忆化搜索是顺推;注意动规 d p dp 总能装换为记搜)。

然后? 没有然后了。你们看不到跌宕起伏的剧情了

顺便提一句,大家觉得我LaTeX修炼得怎么样(E题)?

代码
//发现D题没放代码,赶紧补一下

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,dp[2005][2005];

struct node
{
	int rt;
	int num;
}a[5005];

bool cmp(node a,node b)
{
	return a.num>b.num;
}

int work(int now,int l,int r)
{
	if (l>r)  return a[now].num;
	if (dp[l][r]!=-1)  return dp[l][r];
	
	int first=work(now+1,l+1,r)+a[now].num*abs(a[now].rt-l);
	int second=work(now+1,l,r-1)+a[now].num*abs(a[now].rt-r);
	dp[l][r]=max(dp[l][r],max(first,second));
	
	return dp[l][r];
}

signed main()
{
	cin>>n;
	memset(dp,-1,sizeof(dp));
	for (int i=1;i<=n;i++)  cin>>a[i].num,a[i].rt=i;
	
	sort(a+1,a+n+1,cmp);
	cout<<work(1,1,n)<<endl;
	
	return 0;
}

总结

①排名: 621(中国59)

本次比赛排名较高,总结经验
(1)一些大佬看到Unrated后溜了;
(2)第五题特别难,于是前四题就拼手速;我花了19分钟创纪录切掉 了前四题,但是由于太弱在第五题自闭;
(3)题目偏向于本蒟蒻擅长的数学
(4)赛前进答疑对AT发了一句话:
“I hope I can get a good mark this time!”
(5)认真地上了学校的文化课。

②总分: 1000(赛后1500)
哇哇哇,还是没进步,我真的弱到极致了呀 o ( ) o q w q o(╥﹏╥)o qwq

原创文章 19 获赞 27 访问量 1945

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/105630948