POI2014 Solar lamps

Solar lamps

POI2014

题意

1.有n个灯,他们只会朝一个确定的夹角发光

2.每个灯被放在一个固定的地方,并且朝向同一个方向(指如果发光会朝同一个方向)

3.第i盏灯如果被至少K[i]盏灯照到那么他就会发光

4.现在按从1到n的顺序依次给每盏灯通电,即在第i个时刻给第i盏灯通电

5.问每盏灯在哪个时刻开始发光

1.先转换一下坐标
(方向乱七八糟看着不恶心么)

设新坐标为(a,b)  
(x,y)=a(x1,y1)+b(x2,y2)
x=a*x1+b*x2
y=a*y1+b*y2

a=(-x*y1+y*x1)/(x1*y2-y1*x2)
b=(+x*y2-y*x2)/(x1*y2-y1*x2)

x1*y2-y1*x2 是定值

我们令这个定值为一个整数,就可以保证符号了

2.那么一盏灯(a,b),
可以照亮灯(x,y) {x>=a&&y>=n}

3.可以用树套树解决二维偏序问题,但是树套树要 n log ⁡ n 2 n{\log{n}}^2 nlogn2的内存

4.考虑同时二分

对于每个l,r,L,R  
mid=L+R>>1
1.先按照时间序(mid)分成两份询问

2.再把(l,r)的询问按y升序

3.如果碰到的询问Q[i]
Q[i].id<=mid
或者
当前它前面至少有Q[i].K盏灯已经亮了
那么把它塞入BIT中

4.亮了的(被照亮或被点亮)Q[i]放左边,没亮的放右边(放右边时把K-query(Q[i].y))

这样子不需要每次从头开始扫,每个区间就扫(L,R),因为前面区间可能产生的贡献已经计算过了

5.细节
如果这个照亮的角度是0°

即 两个向量在同一直线上,但是只有两个不共线的向量才能表示出任意向量,那么坐标转换此时就有问题了

但是重新写一下特殊情况有比较麻烦,所以我们强行把这两个向量掰开那么一丢丢角度(小到不影响结果)

具体代码

#include<bits/stdc++.h>
using namespace std;
const int M=200005;
int n,len,ans[M];
long long B[M];
int sum[M];
void add(int x,int a) {
    while(x<=len) {
        sum[x]+=a;
        x+=-x&x;
    }
}
int query(int x) {
    int res=0;
    while(x) {
        res+=sum[x];
        x-=-x&x;
    }
    return res;
}
struct node {
    long long x,y;
    int id,K;
    bool operator <(const node&tmp)const {
        if(x!=tmp.x)return x<tmp.x;
        return y<tmp.y;
    }
} Q[M],hc[M];
void solve(int l,int r,int L,int R) {
    if(l>r||L>R)return;
    int mid=L+R>>1;
    int L1=l,R2=r;
    sort(Q+l,Q+r+1);
    for(int i=l; i<=r; i++) {
        int k=query(Q[i].y);
        if(Q[i].id<=mid||k>=Q[i].K) {
            hc[L1++]=Q[i],ans[Q[i].id]=mid;
            add(Q[i].y,1);
        } else Q[i].K-=k,hc[R2--]=Q[i];
    }
    for(int i=l; i<L1; i++)add(hc[i].y,-1);
    for(int i=l; i<=r; i++)Q[i]=hc[i];
    solve(l,L1-1,L,mid-1);
    solve(R2+1,r,mid+1,R);
}
int main() {
    long long X1,Y1,X2,Y2;
    int x,y;
    scanf("%d %lld %lld %lld %lld",&n,&X1,&Y1,&X2,&Y2);
    int f=1;
    if(X1*Y2-Y1*X2==0) {
        int m=max(abs(X1),abs(Y1));
        int d=(2000000000ll-m+1)/m;
        X1=X1*d+1,Y1=Y1*d;
    }
    if(X1*Y2-Y1*X2<0)f=-f;
    len=0;
    for(int i=1; i<=n; i++) {
        scanf("%d %d",&x,&y);
        long long a=-1ll*x*Y1+1ll*y*X1;
        long long b=+1ll*x*Y2-1ll*y*X2;
        a=a*f,b=b*f;
        Q[i].x=a,Q[i].y=b;
        B[++len]=b;
    }
    sort(B+1,B+1+len);
    len=unique(B+1,B+1+len)-B-1;
    for(int i=1; i<=n; i++) {
        Q[i].id=i;
        Q[i].y=lower_bound(B+1,B+1+len,Q[i].y)-B;
        scanf("%d",&Q[i].K);
    }
    solve(1,n,1,n);
    for(int i=1; i<=n; i++) {
        printf("%d ",ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值