[COCI2009]Dvapravca

题意

给定平面上的 N N 个点,其中有一些是红的 , , 其他是蓝的

现在让你找一对平行线,在满足在平行线之间没有蓝点 , , 并且平行线不经过任何一个点的情况下,使得被夹在平行线之间的红色点个数最多

给出的点没有三点共线的情况 , , 平行线可以不平行坐标轴


题解

做一条垂直于平行线的线,然后把所有点排序后都投影到这条线上形成一个序列

那么考虑在序列上我们要求的就是最长全红子串了

这个可以用线段树直接维护 ; ; 考虑到平行线的角度会变

那么答案就是我们把这条垂线在旋转180的过程中所有投影序列的答案的最大值

我们显然不能直接去旋转 , , 考虑要怎么样投影序列才会发生变化

这里写图片描述

投影序列是{1,2,3,4},那么颜色序列就是 1001, 1001 , 答案是 1 1

这里写图片描述

当投影线是L1时投影序列是 {1,2,3,4} { 1 , 2 , 3 , 4 }

当投影线是 L2 L 2 时投影序列是 {1,2,4,3} { 1 , 2 , 4 , 3 }

也就是说当投影线的斜率从小于两点连线垂线 (Lx) ( L x ) 斜率到大于他的过程中这两个点在投影序列上的位置会互换

因为垂线的斜率和原直线的斜率是一一对应的 , , 那么我们可以直接按照两点之间的连线的斜率排序

那么旋转投影线每次就可以直接旋转到下一个垂线的位置

即使有两点投影位置相同也没关系,因为我们可以微调一下

注意对于所有斜率相同的直线我们需要一次性在线段树上处理完

因为总共有 n2 n 2 条线 , , 所以会发生n2次交换 , , 所以复杂度是O(n2logn)

Hint: H i n t :

连线的方向和转动的方向要一致(需要脑补一下)

斜率判等的精度要高一点

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=1005,M=5e5+5;
const double eps=1e-15;
typedef int arr[N];
typedef double db;
struct po{
    int x,y,col,id;
    inline void in(){sd(x),sd(y),col=gc()=='R';}
    inline bool operator<(const po b)const{return x==b.x?y<b.y:x<b.x;}
    inline db operator/(const po b){return atan2(y-b.y,x-b.x);}
}p[N];
struct Li{
    int a,b;db slope;
    inline bool operator<(const Li T)const{return slope<T.slope;}
    inline bool operator==(const Li b)const{return fabs(slope-b.slope)<eps;}
}L[M];
struct Seg{int l,r,mx,sz;}tr[N<<2];
int n,m,ans,Sz=1;
inline void up(int p,int lc,int rc){
    tr[p].mx=max(tr[lc].r+tr[rc].l,max(tr[lc].mx,tr[rc].mx));
    tr[p].l=tr[lc].l,tr[p].r=tr[rc].r;
    if(tr[lc].l==tr[lc].sz)tr[p].l+=tr[rc].l;
    if(tr[rc].r==tr[rc].sz)tr[p].r+=tr[lc].r;
}
inline void mdy(int p,int x){for(p+=Sz,tr[p]={x,x,x,1},p>>=1;p;p>>=1)up(p,p<<1,p<<1|1);}
inline void upd(Li x){
    if(p[x.a].col^p[x.b].col)
        mdy(p[x.b].id,p[x.a].col),
        mdy(p[x.a].id,p[x.b].col);
    swap(p[x.a].id,p[x.b].id);
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(n);
    fp(i,1,n)p[i].in();sort(p+1,p+n+1);
    fp(i,1,n)p[i].id=i;while(Sz<=n)Sz<<=1;--Sz;
    fp(i,1,n)tr[i+Sz].sz=1,p[i].col?tr[i+Sz]={1,1,1,1},1:0;
    fd(i,Sz,1)tr[i].sz=tr[i<<1].sz+tr[i<<1|1].sz,up(i,i<<1,i<<1|1);
    fp(i,1,n)fp(j,i+1,n)L[++m]={i,j,p[j]/p[i]};
    sort(L+1,L+m+1);
    fp(i,1,m){
        upd(L[i]);
        while(L[i]==L[i+1]&&i<m)upd(L[++i]);
        cmax(ans,tr[1].mx);
    }
    printf("%d\n",ans);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值