AtCoder Beginner Contest 220部分题(G,H)题解

刚开始的时候被E题卡住了,不过发现是个数学题后就开始使劲推式子,幸运的是推出来了,之后的F题更是树形DP换根的模板吧,就草草的过了,看了一眼G,随便口胡了一下,赶紧打代码,毕竟时间不多了,最后也没打完!比赛结束五分钟后,打完代码,woc,连样例都过不去,一看,确实忽略了一些情况。这个题卡的值了!

G - Isosceles Trapezium

题目中有1000个点,每个点都有一定的权值,要求你选出四个点,使得他们的权值和最大并且能够组成一个等腰梯形。
首先我们肯定是要挖掘等腰梯形的性质,来帮助我们做题。最明显的特征就是有两个平行的边,这启示我们在本题中的研究对象从点放到边上,我们可以预处理出所有的线段,考虑到等腰梯形的特殊性,我们考虑怎样的两条线段能够组成等腰梯形。发现他们的垂直平分线相同,并且他们线段的中点不是同一个点。既然这样的话,我们对于每一条线段需要一下信息:
1.它所对应的垂直平分线。
2.它的中点。
3.组成这个线段的两个点的权值和。
由于我们知道只有垂直平分线相同的线段才能组成等腰梯形,所以我们可以将所有的线段按照他们的垂直平分线分组。这就要需要我们制定一个唯一确定垂直平分线的方法,但double的精度问题可能会是这个确定有误差,所以我们用两个整数代表斜率k的分子分母,之后再用一个整数代表b的分子,三个数据就能代表一个线段,之后进行约分,符号统一等。
在组内,我们怎样找中点不同的两个线段的权值最大?我们可以建个set,权值作为第一排序关键字,之后再同一组内的所有线段之和权值最大的线段比较终点相同不相同即可。这个留作自己思考。整体代码的实现需要比较多的语法,挺拓展一个人的语法上的宽度。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010;
int n,nx;
ll X[N],Y[N],W[N];
map<tuple<ll,ll,ll>,int>mp;//用map记录垂直平分线分的组。 
set<pair<ll,pair<ll,ll> > >st[N*N];//用set储存不同组里这个线段的中点和w和。 
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int main()
{
	//freopen("1.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld%lld%lld",&X[i],&Y[i],&W[i]);
	for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
		{
			ll dx=X[i]-X[j],dy=Y[i]-Y[j],dw=W[i]+W[j];
			if(!dx||!dy)
			{
				if(!dx) dy=0,dx=1;
				else dx=0,dy=1;
			}
			else
			{
				ll d=gcd(dx,dy);
				swap(dx,dy);
				dx/=d;dy/=d;//将k的分子分母化作最简。 
				dy*=-1;
				if(dx<0&&dy<0||dx>0&&dy<0) dx=-dx,dy=-dy;//统一符号在dx上. 	
			}
			ll cx=X[i]+X[j],cy=Y[i]+Y[j]; 
			ll b=cy*dx-cx*dy;//求得b,这只是b的分子,b的分母为2*dx,但这之前存过dx,dy,所以不必单独存储b的分母
			auto sx=make_tuple(dx,dy,b); 
			if(mp.find(sx)==mp.end()) mp[sx]=++nx;
			st[mp[sx]].insert({-dw,{cx,cy}});
		}
	ll ans=-1;
	for(int i=1;i<=nx;++i)//对每一组进行搜索答案。
	{
		if(st[i].size()<2) continue;
		auto sx=*(st[i].begin());
		for(auto x:st[i])
		{
			if(x.second!=sx.second) 
			{
				ans=max(ans,-sx.first-x.first);
				break;
			}
		} 
	}	
	printf("%lld",ans);
	return 0;	
} 
上一篇:AtCoder Regular Contest 126题解(A-C)


下一篇:AtCoder Beginner Contest 182 F