BZOJ3833[Poi2014] Solar lamps

Task:

有很多盏灯,第 i 盏灯在第 i 个时刻点亮。如果有至少 ki 盏灯照亮了第 i 盏灯,那么这盏灯就将被点亮。每盏灯照亮的范围都是一样角度的一个区域且无限延伸,给定 (x1,y1),(x2,y2) 表示如果这盏灯的坐标在 (x,y) ,那么它的照亮范围为射线 (x,y)(x+x1,y+y1) 与射线 (x,y)(x+x2,y+y2) 之间的区域。

给定 n 盏灯的坐标, ki ,求每一盏灯被点亮的最早时间。 (n<=200000)

BZOJ3833[Poi2014] Solar lamps_第1张图片

Solution:

kxj Orz…

首先所有灯的朝向都是一样的,所以可以直接扭转坐标轴,使得变成一个二维的平面问题。题目非常人性地直接给出了两个向量,这里可以使用这两个向量为基底直接表示平面上的点,转化为一个平面直角坐标系:

(ai,bi)ai(x1,y1)+bi(x2,y2)=(xi,yi){aix1+bix2=xiaiy1+biy2=yiai=xiy2x2yix1y2x2y1bi=x1yixiy1x1y2x2y1

于是我们惊奇的发现分母是一样的,于是可以直接忽略分母。然而还有一个特判的点,本题中光线可能只有一条线,那么将一个向量微小偏移一下,不影响结果,就可以了。

然后现在是一个平面直角坐标系,对于每一个点,它能照亮的区域是右上角的区域。有一种整体二分的做法:

直接二分时间 [L,R] 同时 [l,r] 为在此段时间内点亮的灯,取 Mid=L+R2 ,将 [l,r] 划分为 [l,mid] [mid+1,r] 表示在 Mid 前点亮的灯与在 Mid 后点亮的灯,递归求解即可。

那么现在问题是如何求一盏灯是否在 Mid 前点亮,关键在于求一盏灯的左下角的灯的数目,这在平面上是一个非常经典的问题。将灯按照 x 坐标排序,用树状数组维护 y 坐标,横向依次扫描就可以完成了。如果求得的左下角的灯的个数大于等于 k 个,或者灯的编号大于等于 Mid ,则把它归到 [l,mid] 的区间。

于是就可以解决这道题了,复杂度 O(nlog2n)

#include
#include
#include
#define ll long long
#define lowbit(x) ((x)&(-x))
using namespace std;
inline void Rd(int &res){
    char c;int f=1;res=0;
    while(c=getchar(),c<'0'&&c!='-');
    if(c=='-')f=-1,c=getchar();
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),c>='0');
    res*=f;
}
const int M=200005;
struct Node{
    int x,y,k,ans,id;
    bool operator <(const Node &A)const{
        if(x!=A.x)return xelse return yint n;
inline bool cmp(Node A,Node B){return A.idinline void Change(int &x,int &y){//偏移向量
    int mx=max(abs(x),abs(y));
    int d=((ll)2e9+mx-1)/mx;
    x*=d;y*=d;
    x++;
}
void Init(){
    for(int i=1;i<=n;i++)Data[i-1]=X[i];
    sort(Data,Data+n);
    int sz=unique(Data,Data+n)-Data;
    for(int i=1;i<=n;i++)
        A[i].x=lower_bound(Data,Data+sz,X[i])-Data+1;//[1,sz]
    for(int i=1;i<=n;i++)Data[i-1]=Y[i];
    sort(Data,Data+n);
    sz=unique(Data,Data+n)-Data;
    for(int i=1;i<=n;i++)
        A[i].y=lower_bound(Data,Data+sz,Y[i])-Data+1;//[1,sz]
}
struct BIT{
    int val[M];
    void Add(int x,int v){
        while(x<=n){
            val[x]+=v;
            x+=lowbit(x);
        }
    }
    int Query(int x){
        int rs=0;
        while(x){
            rs+=val[x];
            x-=lowbit(x);
        }
        return rs;
    }
}BIT;
void solve(int L,int R,int l,int r){
    if(L==R){
        for(int i=l;i<=r;i++)A[i].ans=L;
        return;
    }
    int Mid=(L+R)>>1,mid=l-1;
    sort(A+l,A+r+1);
    for(int i=l;i<=r;i++){
        int rs=BIT.Query(A[i].y);
        if(A[i].id<=Mid||rs>=A[i].k){
            BIT.Add(A[i].y,1);
            mid++;swap(A[mid],A[i]);
        }else A[i].k-=rs;
    }
    for(int i=l;i<=mid;i++)BIT.Add(A[i].y,-1);
    if(l<=mid)solve(L,Mid,l,mid);
    if(mid+1<=r)solve(Mid+1,R,mid+1,r);
}
int main(){
    int x1,y1,x2,y2;
    Rd(n);
    Rd(x1),Rd(y1),Rd(x2),Rd(y2);
    if(1LL*x1*y2==1LL*x2*y1)Change(x1,y1);
    if(1LL*x1*y2<1LL*x2*y1)swap(x1,x2),swap(y1,y2);
    for(int i=1;i<=n;i++){
        int x,y;
        Rd(x),Rd(y);
        A[i].id=i;
        X[i]=1LL*x*y2-1LL*x2*y;
        Y[i]=1LL*x1*y-1LL*x*y1;
    }
    for(int i=1;i<=n;i++)Rd(A[i].k);
    Init();
    solve(1,n,1,n);
    sort(A+1,A+n+1,cmp);
    for(int i=1;i<=n;i++)
        printf("%d%c",A[i].ans,i==n?'\n':' ');
    return 0;
}

你可能感兴趣的:(POI,BZOJ,数学,-----几何,-----,-----扫描线,数据结构,-----树状数组,整体二分)