1. 程式人生 > >[BZOJ5099][POI2018]Pionek(極角排序+two pointers)

[BZOJ5099][POI2018]Pionek(極角排序+two pointers)

code atan bsp 宋體 typedef lld max con 中間

幾個不會嚴謹證明的結論:

1.將所有向量按極角排序,則答案集合一定是連續的一段。

當答案方向確定時,則一個向量會被選入答案集合當且僅當向量在答案方向上的投影一定都是正的

所以,兩個選中向量中間隔著一個向量,則必然可以將後面所有選中向量均前移一位並使答案不劣。

2.答案集合中不存在兩個向量的極角差超過$\pi$。

顯然需要保證的是,答案集合中任意兩個向量投影不為負值,否則一定可以通過刪去其中一個使答案更優。

3.當選擇的向量集合區間左端點增加時,右端點單調不減。

感性理解,答案向量的極角會在選擇的向量區間中某兩個相鄰向量極角之間。當區間左端點增加時,答案向量的極角必然增加,故右端點不會減少。

於是,將所有向量極角排序後,對所有掃過的極角極差不超過$\pi$的向量區間更新答案即可。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=400010;
 9 const double pi=acos(-1.);
10 int
n; 11 ll ans; 12 struct P{ 13 int x,y; double s; 14 bool operator <(const P &a)const{ return s<a.s; } 15 P operator +(const P &a)const{ return (P){x+a.x,y+a.y}; } 16 P operator -(const P &a)const{ return (P){x-a.x,y-a.y}; } 17 ll len(){ return 1ll*x*x+1ll*y*y; }
18 }a[N]; 19 20 int main(){ 21 freopen("bzoj5099.in","r",stdin); 22 freopen("bzoj5099.out","w",stdout); 23 scanf("%d",&n); 24 rep(i,1,n) scanf("%d%d",&a[i].x,&a[i].y),a[i].s=atan2(a[i].x,a[i].y); 25 sort(a+1,a+n+1); P cur=(P){0,0}; int x=0; 26 rep(i,1,n) a[i+n]=a[i],a[i+n].s+=2*pi; 27 rep(i,1,n){ 28 while (x<n*2 && a[x+1].s-a[i].s<=pi) cur=cur+a[++x],ans=max(ans,cur.len()); 29 cur=cur-a[i]; ans=max(ans,cur.len()); 30 } 31 printf("%lld\n",ans); 32 return 0; 33 }

[BZOJ5099][POI2018]Pionek(極角排序+two pointers)