ABC163(Atcoder beginner contest 163)部分题解

本文详细解析了五道ACM竞赛题目,涵盖了π值计算、模拟算法、统计分析、复杂度优化及区间动态规划等内容,提供了高效算法实现思路。

题解

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×105N≤2×10^5N2×1051≤K≤N+11≤K≤N+11KN+1

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

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

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

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

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

r=Σj=n−i+1njr=Σ_{j=n-i+1}^n jr=Σj=ni+1nj
l=Σj=0i−1jl=Σ_{j=0}^{i-1} jl=Σj=0i1j

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

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

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

#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


前言

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

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

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


思路(正解)

赛后诸葛亮

考虑贪心。

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

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

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

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

后记

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

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

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

顺便提一句,大家觉得我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(╥﹏╥)oqwqo(╥﹏╥)o qwqo()oqwq

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值