[联合集训6-22] 路灯 整体二分+扫描线树状数组

先给每个点重新设一个坐标(就是把给定的两个边界强行定成横纵坐标找),这个坐标可以直接通过和两个边界叉积得到。
于是点 i 的答案就是其左下方所有点答案的第 k i 小值,如果不足 k i 个点答案就是 i
于是我们可以考虑整体二分,二分一个时间 M i d ,把编号 M i d 的点强行点亮,看剩下的点中有多少个也能跟着亮。这个二维数点可以横纵坐标分别为一二关键字排序之后,用扫描线+树状数组解决,要注意确定一个点能被点亮之后要立即把这个点加进去扫描。
那么我们就在编号 > M i d 的点中确定出哪些点答案 M i d ,哪些点 > M i d ,我们把后者的 k i 减去前者带来的贡献,分治下去即可。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
#define ll long long
using namespace std;
int n,w[N],s[N],ans[N];
ll z[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct pt
{
    ll x,y;int id;
    void get(int d)
    {
        x=read();y=read();id=d;
    }
    ll operator *(pt b)
    {
        return x*b.y-y*b.x;
    }

}L1,L2,a[N],b[N],q[N];
bool cmp(pt a,pt b) 
{
    if(a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}
struct bit
{
    int c[N];
    void add(int x,int d)
    {
        for(;x<=n;x+=(x&-x)) 
            c[x]+=d;
    }
    int qry(int x)
    {
        int r=0;
        for(;x;x-=(x&-x))
            r+=c[x];
        return r;
    }
}T;
void solve(int L,int R,int l,int r)
{
    if(l>r) return ;

    if(L==R)
    {
        for(int i=l;i<=r;i++)
            ans[a[i].id]=L;
        return ;    
    }
    int Mid=(L+R>>1),mid=l-1,len=r-l+1; 
    for(int i=r;i>=l;i--)
        if(a[i].id<=Mid) {mid=i;break;} 
    for(int i=l;i<=r;i++)
        q[i-l+1].x=a[i].x;      
    for(int i=l;i<=r;i++)
        z[i-l+1]=a[i].y;
    sort(z+1,z+len+1);
    for(int i=l;i<=r;i++)
        q[i-l+1].y=lower_bound(z+1,z+len+1,a[i].y)-z;

    for(int i=l;i<=mid;i++)
        q[i-l+1].id=0;
    for(int i=mid+1;i<=r;i++)
        q[i-l+1].id=a[i].id,s[a[i].id]=w[a[i].id];      
    sort(q+1,q+len+1,cmp);  

    for(int i=1;i<=len;i++)
        if(q[i].id)
        {   
            s[q[i].id]-=T.qry(q[i].y);
            if(s[q[i].id]<=0) T.add(q[i].y,1);

        }
        else T.add(q[i].y,1);

    for(int i=1;i<=len;i++)
        if((!q[i].id)||s[q[i].id]<=0) T.add(q[i].y,-1);

    int tp=mid,ddd;
    for(int i=mid+1;i<=r;i++)
        if(s[a[i].id]<=0) b[++tp]=a[i];
    ddd=tp;
    for(int i=mid+1;i<=r;i++)
        if(s[a[i].id]>0) w[a[i].id]=s[a[i].id],b[++tp]=a[i];
    for(int i=mid+1;i<=r;i++)
        a[i]=b[i];

    solve(L,Mid,l,ddd);
    solve(Mid+1,R,ddd+1,r); 


}
int main()
{
    n=read();
    L1.get(0);L2.get(0);
    if(L1*L2==0)
    {
        ll tmp=0x3f3f3f3f/max(L1.x,L1.y);
        L1.x*=tmp;L1.y*=tmp;
        L2=L1;L2.x++;
    }
    if(L1*L2>0) swap(L1,L2);
    for(int i=1;i<=n;i++)
        a[i].get(i);

    for(int i=1;i<=n;i++)
        w[i]=read();    

    for(int i=1;i<=n;i++)
        a[i]=(pt){a[i]*L1,L2*a[i],a[i].id};

    solve(1,n,1,n);
    for(int i=1;i<=n;i++)
        printf("%d ",ans[i]);   
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dofypxy/article/details/80779161