Atcoder beginner contest 168

开头话过于消极,讨论后决定删除。

题目编号 做题时间 难度 状态
A 2min 入门 AC
B 2min 入门 AC
C 18min 普及- AC
D 28min 普及/提高- AC(Penalty=2)
E 31min 普及+/提高 赛后AC
F inf 普及+/提高 未提交

A

Solution

小模拟题,注意看清题面并谨慎代码,小心失误罚时。

#include 
#define int long long
using namespace std;

int k;
string s;

signed main()
{
	cin>>s;
	k=s[s.size()-1]-'0';
	
	if (k==2||k==4||k==5||k==7||k==9)  cout<<"hon"<<endl;
	else if (k==0||k==1||k==6||k==8)  cout<<"pon"<<endl;
	else if (k==3)  cout<<"bon"<<endl;
	
	return 0;
}

B

Solution

简单的字符串模拟操作。

Code

#include 
#define int long long
using namespace std;

int k;
string s;

signed main()
{
	cin>>k>>s;
	if (s.size()<=k)  cout<<s<<endl;
	else
	{
		for (int i=0;i<=k-1;i++)  cout<<s[i];
		cout<<"..."<<endl;
	}
	return 0;
}

C

Chatting

需要余弦定理的前置芝士,给了大佬LSH一个展现的机会。

但是,由于本蒟蒻有在考试中坚决不开聊天软件的喜欢,本蒟蒻在比赛中依靠一位女学霸讲解了三角函数余弦定理详解A掉了这题。

Solution

容易发现,我们现在已经知道了时针的长度,即三角形的一边 a a a;也知道了分针的长度 b b b,即三角形的另一边;如果我们求出了上述两条边的夹角,那么就能求出第三边的长度。不要问为什么,继续看下去。

容易发现,时针12小时转360°,所以它一小时转30°;同理,由于分针60分钟转360°,所以它一分钟转6°。

所以,时针转的角度就是小时数×30;其中小时数就是 h + m 60 h+\frac m {60} h+60m,所以时针转动了 30 ( h + m 60 ) ° 30(h+\frac m {60})° 30(h+60m)°

由于分针转一个小时会回到原来的位置,所以分针共转了 6 m ° 6m° 6m°

得到转动后时针与分针的夹角就是 ∣ 30 ( h + m 60 ) − 6 m ∣ |30(h+\frac m {60})-6m| 30(h+60m)6m°,注意用取绝对值的方式保留上述值得非负性

为了表述方便,这里记夹角度数 ∣ 30 ( h + m 60 ) − 6 m ∣ |30(h+\frac m {60})-6m| 30(h+60m)6m d e de de,那么答案就是 a 2 {a^2} a2 + + + b 2 {b^2} b2 − - 2 a b c o s ( d e ) 2abcos(de) 2abcos(de)。这就是许多高中生与初三学生熟悉的余弦定理?可以吃吗(逃)

注意C++cmath库中的 c o s cos cos函数的参数采用弧度制(这个本蒟蒻倒是知道一点),所以要这么计算一个角度的cos值(先转换为弧度制再计算):

inline double COS(double tmp)//注意返回的是double量
{
	double k=tmp*(M_PI)/180.00;//转换为弧度制
	return cos(k);//然后直接调用Cmath中的cos函数并返回
}

最后提醒几个重要的东西:

①不用万能头文件的大佬一定要加 c m a t h cmath cmath头文件;

②一定要转换为弧度制;

③余弦公式别背错;

如果您并没有学到三角函数中的余弦定理因此没做出来,千万不要灰心!

上代码~

#include 
#define int long long
using namespace std;

double a,b,n,m,de;

inline double pi()
{
	return M_PI;
}

inline double COS(double tmp)
{
	double k=tmp*pi()/180.00;
	return cos(k);
}

signed main()
{
	cin>>a>>b>>n>>m;
	de=double((30.00*n+(m/60.00)*30.00)-6.00*m);
	if (2*de>360)  de=360-de;
	double now=1.00*a*a+1.00*b*b-2.00*a*b*COS(de);
	cout<<fixed<<setprecision(10)<<sqrt(now)<<endl;
	
	return 0;
}

D

Solution

这是一道裸的 D i j k s t r a + H e a p Dijkstra+Heap Dijkstra+Heap

直接跑 D i j k s t r a + H e a p Dijkstra+Heap Dijkstra+Heap,在松弛(relax)的过程中,记录下 u u u号房间被 v v v号房间松弛了,那么就相当于在 u u u号房间放了一个指向 v v v号房间的指示牌。

就像这样:

if (dis[y]>dis[x]+e[i].dis)
{
	ans[y]=x;
	dis[y]=dis[x]+e[i].dis;
	if (!visited[y])  q.push((node){dis[y],y});
}

注意,如果 k ( 2 ≤ k ≤ n ) k(2≤k≤n) k(2kn)号房间无法到达 1 1 1号房间,那么显然应该输出"No"。

时间复杂度 O ( ( n + m ) l o g 2 n ) O((n+m)log_2n) O((n+m)log2n)

#include 
#define int long long
#define inf 20000000007
using namespace std;

int n,m,s=1,cnt=0;
int head[200005],dis[100005],visited[100005],ans[100005];

struct edgde
{
	int next;
	int to;
	int dis;
}e[500005];

struct node
{
	int dis;
	int pos;
	bool operator < (const node x) const
	{
		return x.dis<dis;
	}
};

std::priority_queue<node> q;

inline void add_edge(int u,int v,int w)
{
	cnt++;
	e[cnt].to=v;
	e[cnt].dis=w;
	e[cnt].next=head[u];
	head[u]=cnt;
}

inline void dijkstra()
{
	dis[s]=0;
	q.push((node){0,s});
	
	while (!q.empty())
	{
		node tmp=q.top();
		q.pop();
		
		int x=tmp.pos;
		if (visited[x])  continue;
		visited[x]=1;
		
		for (int i=head[x];i;i=e[i].next)
		{
			int y=e[i].to;
			if (dis[y]>dis[x]+e[i].dis)
			{
				ans[y]=x;
				dis[y]=dis[x]+e[i].dis;
				if (!visited[y])  q.push((node){dis[y],y});
			}
		}
	}
}

signed main()
{
	cin>>n>>m;
	for (int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		add_edge(u,v,1);
		add_edge(v,u,1);
	}
	for (int i=1;i<=n;i++)  dis[i]=inf;
	
	dijkstra();
	
	for (int i=2;i<=n;i++)
	{
		if (ans[i]==0)  return cout<<"No"<<endl,0;
	}
	cout<<"Yes"<<endl;
	for (int i=2;i<=n;i++)  cout<<ans[i]<<endl;
	return 0;
}

E

呜呜,我太弱了o(╥﹏╥)o

比赛的时候紧张,没看懂题目导致看不懂第二个样例,就没有开始想思路……自闭ing……

当然,在讲解开始前,先感谢一下 b 6 e 0 b6e0 b6e0大佬对E题的解释,否则我永远做不出来这题QwQ

Solution

细细研究一下这个可爱的式子:
a i × a j + b i × b j = 0 a_i×a_j+b_i×b_j=0 ai×aj+bi×bj=0

移项,得 a i × a j = − b i × b j a_i×a_j=-b_i×b_j ai×aj=bi×bj
再移项,得 a i b i = − b j b i \frac {a_i} {b_i}=-\frac {b_j} {b_i} biai=bibj

现在我们得到了看似更好的表达方式,于是我们开始思考如何解除这题。首先预处理出所有的 a i b i \frac {a_i} {b_i} biai,然后从中挑出一些作为被选择当然在选择的那些中不能存在两个乘积为-1的;然后输出方案数即可。

于是,现在难点在于如何做到这些。首先,在输入进来的时候将每个 ( a i , b i ) (a_i,b_i) (ai,bi)约分。并且用一个 m a p map map记录下每组出现过的 ( a i , b i ) (a_i,b_i) (ai,bi)出现的次数。

假设目前看到了 ( x , y ) (x,y) (x,y)这种情况存在 k k k个,并且不存在 ( x 2 , y 2 ) (x2,y2) (x2,y2)使得 x y × x 2 y 2 = − 1 \frac {x} {y}×\frac{x2}{y2}=-1 yx×y2x2=1,那么显然存在 2 k 2^k 2k种方案(对于每个属于该类的三文鱼选或不选)。

如果存在 ( x 2 , y 2 ) (x2,y2) (x2,y2)使得 x y × x 2 y 2 = − 1 \frac {x} {y}×\frac{x2}{y2}=-1 yx×y2x2=1呢?假设存在 k 2 k2 k2 ( x 2 , y 2 ) (x2,y2) (x2,y2)

显然,我们只能选择两种中的一个,看起来答案是 2 k 2^k 2k + + + 2 k 2 2^{k2} 2k2,但事实上两种方案都不选的方案被多算了一次,所以此时的方案数应该是 2 k 2^k 2k + + + 2 k 2 − 1 2^{k2}-1 2k21

综上所述,答案就是所有方案数的乘积。

注意!!!

①要减去所有都不选的情况;

②如果存在 i i i使得 a i = b i = 0 a_i=b_i=0 ai=bi=0,那么只要选了这个其他的就都不能选了!所以刚开始要忽略这种情况,但是记得最后要加上!

如果您被注意点②坑了,请认真读一下代码的第29-33行与第56行。

③不要忘记取模与减法+取模的方式。

上代码~

#include 
#define int long long
using namespace std;
const int mod=1e9+7;

int n,tot=1,cnt_all_zero=0,ans=1;

map<pair<int,int>,int> m;
map<pair<int,int>,int>::iterator it;

int quick_power(int a,int b)
{
	int res=1ll;
	for (;b;b=b>>1,a=(a*a)%mod)
	{
		if (b&1)  res=(res*a)%mod; 
	}
	return res;
}

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		int u,v,now;
		cin>>u>>v;
		
		if (u==0&&v==0)
		{
			cnt_all_zero++;
			continue;
		}
		now=__gcd(u,v);
		u/=now,v/=now;
		
		if (u<0)  u=-u,v=-v;
		m[make_pair(u,v)]++;
	}
	for (it=m.begin();it!=m.end();it++)
	{
		if (it->second==0)  continue;
		int x=it->first.first,y=it->first.second;
		int p=quick_power(2,m[make_pair(x,y)])%mod;
		
		y=-y;
		if (y<0)  x=-x,y=-y;
		
		if (m.count(make_pair(y,x)))
		{
			p=(p+quick_power(2,m[make_pair(y,x)])-1)%mod;
			m[make_pair(y,x)]=0;
		}
		ans=(ans*p)%mod;
	}
	cout<<((ans+cnt_all_zero-1)%mod+mod)%mod<<endl;
	
	return 0;
}

F

Solution

首先,对于大部分的非复杂计算几何题有两种做法:

①数学做法;
②离散化后 d f s dfs dfs b f s bfs bfs

显然数学做法难想,那么我们就往②的方向想。首先,离散化的作用是让所有的线都靠在一起,这样我们再 b f s bfs bfs而不是 d f s dfs dfs(原因下面会说)就可以在 O ( n m ) O(nm) O(nm)的代价中求得奶牛们最大能够走动的面积。

之所以不能 d f s dfs dfs,是因为本题中 d f s dfs dfs会出现超高的时间复杂度(不信您试)。

Code

细节太多咕咕咕

总结

还是太弱了,在C,D题上花了太多的时间,导致做后面的题目的时间过于紧张然后因此发抖,最后把题目读错导致没有开始思考第五题的正解。

反思

①有信心了再提交,我第一次交 D D D题的时候竟然把洛谷上 D i j k s t r a + H e a p Dijkstra+Heap Dijkstra+Heap的模板一字不动地交了!天知道我怎么想的。

②读题能力很有问题。D题读错好不容易发现,E题因为读错题目白白失掉500分!菜死了~

③千万不要紧张!不要认为自己做什么题目都是30分钟以外搞定!献上五个记录:

(1)CF模拟赛中,32分钟切掉Div.3的前五题;

(2)AT模拟赛中,17分钟AK 某场ABC;

所以说,自信很重要!

你可能感兴趣的:(数学,自闭场,数据结构)