http://acm.hdu.edu.cn/showproblem.php?pid=6127
题意:
有n个点,每个点有一个$(x,y)$坐标和一个权值,任意两点之间都有连线,并且连线的权值为两个顶点的。现在画一条直线,求穿过的直线的权值和最大为多少。
思路:
直线将这些点分成了两个部分,然后你可以发现这两个部分之间所有直线的权值和为他们各部分的权值和的乘积。然后我们将所有点按极角排序,预处理一个前缀和,然后用扫描线扫描一圈即可。
我的做法是每次扫描一下点的个数,然后利用前缀和去计算。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 #include<set> 12 using namespace std; 13 14 #define MAXN 50005 15 #define LL long long 16 17 struct Point 18 { 19 LL x,y; 20 int v; 21 double rad; 22 bool operator < (const Point&rhs) const 23 { 24 return rad < rhs.rad; 25 } 26 }p[MAXN]; 27 28 LL sum[MAXN]; 29 30 bool left(Point a, Point b) 31 { 32 return (LL)a.x*b.y - (LL)a.y*b.x >= 0; 33 } 34 35 int main() 36 { 37 int n,m,T; 38 //freopen("in.txt","r",stdin); 39 scanf("%d",&T); 40 while(T--) 41 { 42 scanf("%d",&n); 43 for(int i=0;i<n;i++) 44 { 45 scanf("%I64d%I64d%d",&p[i].x,&p[i].y,&p[i].v); 46 p[i].rad = atan2(p[i].y, p[i].x); 47 } 48 sort(p,p+n); 49 sum[0]=p[0].v; 50 for(int i=1;i<n;i++) sum[i]=sum[i-1]+p[i].v; 51 52 LL ans=0; 53 LL L = 0, R = 0, cnt=0; 54 while (L < n) //每个点都尝试与原点成为分割线 55 { 56 if (R == L) { R = (R + 1) % n; cnt++; } //空区域,数量+1,后面还会减去的 57 while (R != L && left(p[L], p[R])) //R不等于L并且在180度之内 58 { 59 R = (R + 1) % n; 60 cnt++; 61 } 62 63 cnt--; //分隔线旋转,原本在分隔线上的点到了右边,所以要减去 64 //可以理解为将该点分在分隔线的下方 65 66 LL t1,t2; 67 int num=L+cnt; 68 if(num<n) 69 { 70 t1=sum[num]-sum[L]; 71 t2=sum[n-1]-t1; 72 ans=max(ans,t1*t2); 73 } 74 else 75 { 76 t1=sum[n-1]-sum[L]+sum[num-(n-1)-1]; 77 t2=sum[n-1]-t1; 78 ans=max(ans,t1*t2); 79 } 80 L++; //分隔线旋转 81 } 82 printf("%I64d ",ans); 83 } 84 return 0; 85 }