HDU6127 原题传送门
题意:给定n个平面坐标点,任意两个点两两连线(连线不过原点),每个点带权值,然后任意两个点所连成的线段的权值为点权的乘积,找一条过原点的直线,要求它所穿过的线段的权值和最大,输出最大权值和
官方题解:对于一条直线,线段权值和实际上就等于其两边点权和的乘积,所以把所有点按极角排个序,然后扫一圈就好了。(喵了个咪)
个人理解:一直线把所有的点分为直线上方和直线下方两部分,然后每一个点都跟对面的所有点衔接,产生的权值就是对面的权值和乘该点的权值,因此这条直线的经过的线段的权值和就是一部分点的权值和乘于另外一部分的权值和。
而每一个点跟原点的连线都可以将所有点分为两部分(题意不存在两点连线过原点),因此就是直接从点出发找直线将点分为两部分,但如果是枚举每个点再暴力枚举把所有点两部分的话,这样的复杂度是O(n^2), 因此需要对点进行处理也就是极角排序O(n logn)。
排完了序,所有的点就按照逆时针摆好,sum是所有点的权值和,用up记录直线上方的权值和(把当前点也归到直线上放的部分),枚举所有的点和原点连线作为划分的标准有pos记录每次划分到哪个点,然后枚举下一个点的时候, 把上一个点分到另一部分即up减去上一个点的权值,而pos继续扫,最后就是一个ans一直维护up*(sum-up)的最大值,然后输出ans
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5+5e4;
struct point
{
ll x, y, val;
}a[N];
ll max(ll a, ll b)
{
return a>b?a:b;
}
ll Xmul(point a, point b)
{//叉积排序
return a.x*b.y-a.y*b.x;
}
bool cmp(point a,point b)
{
return Xmul(a, b)>0;
}
int main()
{
ios::sync_with_stdio(false);
int t; cin>>t;
while(t--)
{
int n; cin>>n;
ll ans = 0, pos = 1, up = 0, sum = 0;
for(int i = 0; i < n; i ++)
{
cin>>a[i].x>>a[i].y>>a[i].val;
sum += a[i].val;
}
sort(a, a+n, cmp);
up += a[0].val;
for(int i = 0; i < n; i ++)
{
while(cmp(a[i], a[pos]))
{//a[i]跟原点连接为当前直线
up += a[pos].val;
pos ++;
pos %= n;//pos会扫两圈
}
ans = max(ans, up*(sum-up));
up -= a[i].val;
}
cout<<ans<<endl;
}
return 0;
}
顺便附上标程,上面的代码是以叉积的大小作为极角的排序规则,而下面的代码是以tan角作为排序规则,想知道差异在哪不妨把排完序的点输出来看看
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cassert>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef pair<int,int> pi;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define per(i,a,b) for (int i=(a);i>=(b);i--)
#define Rep(i,a,b) for (int i=(a);i<(b);i++)
#define Per(i,a,b) for (int i=(a);i>(b);i--)
//debug
#define deb printf("begin\n");
#define dee printf("end\n");
#define def printf("find\n");
#define dey printf("Yes\n");
#define den printf("No\n");
#define dew printf("wrong\n");
void read(int&x){
x=0;int f=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if (ch=='-')f=-1,ch=getchar();
while (ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
x*=f;
}
//--------------------------head--------------------------//
#define eps 1e-9
#define maxn 200005
struct Node{
double x,y,ang;int v;Node(){}Node(double x,double y,int v):x(x),y(y),v(v){}
void readin(){scanf("%lf%lf\n",&x,&y);read(v);ang=atan(y/x);}
bool operator <(const Node&p)const{return ang<p.ang;}
}p[maxn];
int n,T;double x[maxn],y[maxn];int v[maxn];
ll sumL,sumR,ans;
void work(){
read(n);rep(i,1,n)p[i].readin();sort(p+1,p+n+1);
sumL=sumR=ans=0;rep(i,1,n)if (p[i].x>eps)sumL+=p[i].v;else sumR+=p[i].v;ans=sumL*sumR;
rep(i,1,n){
if (p[i].x>0)sumL-=p[i].v,sumR+=p[i].v;else sumL+=p[i].v,sumR-=p[i].v;
ans=max(ans,sumL*sumR);
}
printf("%lld\n",ans);
}
int main(){
freopen("H.in","r",stdin);
freopen("H_.out","w",stdout);
read(T);while (T--)work();return 0;
}