bzoj 5099: [POI2018]Pionek

    挺好的题。。

    我只想到了按照极角排序之后最优的选择方案一定是连续的一段(注意这是一个圆所以要拓展两倍)。

    证明:如果我们知道了最终向量的方向,那么显然的一点是我们只会选择那些极角差小于$pi /2$的向量(这个可以用向量的加减法来证长度的变化)。

    然后就是优化了。我们枚举所有的$[l,r]$里面包括很多无法达成的情况,即不存在一个最终向量使得选取的区间恰好是$[l,r]$。正解的方法是维护一个pos代表右端点为i时,满足$atan2(y[r],x[r])-atan2(y[l],x[l])<=pi$的最左端点。然后在$++pos$的过程中用$[pos+1,i-1]$来更新答案。这样是$O(n)$的。

    证明:我们只需要证明对于一个左端点$l$,满足$atan2(y[r+1],x[r+1])-atan2(y[l-1],x[l-1])<pi$时,$[l,r]$不可能满足条件即可。这个很显然了。。一半大于$pi /2$那么另一半一定小于$pi /2$的。

#include <bits/stdc++.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define FOR2(a,b,i) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
inline int read() {
    int f=1,sum=0;
    char x=getchar();
    for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
    for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
    return f*sum;
}

#define M 400005
int n;
ll ans;
int ax[M],ay[M];
const double pi=acos(-1);
struct node {
    int x,y;
    double k;
    
    inline void in() {
        x=read(),y=read(),k=atan2(y,x);
    }
    inline bool operator <(const node &a) const {
        return k<a.k;
    }
}a[M];

inline void check(int l,int r) {
    ll t=1ll*(ax[r]-ax[l-1])*(ax[r]-ax[l-1])+1ll*(ay[r]-ay[l-1])*(ay[r]-ay[l-1]);
    ans=max(ans,t);
}

int main() {
    //freopen("a.in","r",stdin);
    n=read();
    for1(1,n,i) a[i].in();
    sort(a+1,a+n+1);
    for1(1,n,i) a[n+i]=a[i],a[n+i].k=a[i].k+2*pi;
    for1(1,2*n,i) ax[i]=ax[i-1]+a[i].x,ay[i]=ay[i-1]+a[i].y;
    int h=1;
    for1(1,2*n,i) {
        while (h<i&&a[h].k+pi<a[i].k) ++h,check(h,i-1);
        check(h,i);
    }
    cout<<ans<<endl;
}
原文地址:https://www.cnblogs.com/asd123www/p/9596185.html