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) 。
Solution:
kxj Orz…
首先所有灯的朝向都是一样的,所以可以直接扭转坐标轴,使得变成一个二维的平面问题。题目非常人性地直接给出了两个向量,这里可以使用这两个向量为基底直接表示平面上的点,转化为一个平面直角坐标系:
然后现在是一个平面直角坐标系,对于每一个点,它能照亮的区域是右上角的区域。有一种整体二分的做法:
直接二分时间 [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;
}