BZOJ2092 : [Poi2010]Lamp

假设从光源$(0,0)$射出的一束光第一次碰到右边墙上的$(x,y)$,那么第$k$次的坐标是$(kx,ky)$。

如果在第$k$次到达了左边某个点且$k$是$2^d\times o$的形式,其中$o$是奇数,那么在第$2^d$次一定也能到达这个点。

枚举所有不超过最大坐标两倍的$2^d$,判断左边每个窗户在$2^d$次能否被照亮。

那么右边要存在一个矩形覆盖$(\frac{x}{2^d-1},\frac{y}{2^d-1})$,左边要存在一个矩形覆盖$(\frac{x}{2^d-2},\frac{y}{2^d-2})$,右边要存在一个矩形覆盖$(\frac{x}{2^d-3},\frac{y}{2^d-3})$,左边要存在一个矩形覆盖$(\frac{x}{2^d-4},\frac{y}{2^d-4})$...

把每个矩形缩放成$O(2^d)$份,并把左右两面墙合并,那么如果左边某个矩形内部存在一个至少被$2^d$个矩形覆盖的点则可行,注意这里可以用分数类做到全整数计算。

扫描线+线段树维护扫描线上每个区间内被覆盖次数的最大值,同时将每个左边的矩形拆成线段树上$O(\log n)$条线段放入。

每次处理完修改操作后,从线段树根节点开始遍历,如果区间最大值$<2^d$或者当前子树内没有任何线段则return,这样即可在总计$O(n\log n)$的时间里找出所有被照亮的矩形。

时间复杂度$O(nw\log n)$,其中$w$为坐标范围。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=605,M=4200005,MX=1025;
int n,m,K,i,ans[N],fin,cy,ce,_,f[MX],C,D,P,pl[N<<1][MX],pr[N<<1][MX];
int py[1400000],l[N<<1][MX],r[N<<1][MX],del[N],cur;
bool has[M];int tag[M],val[M],g[M],v[30005],nxt[30005],ed;
struct Rect{int x1,y1,x2,y2;}a[N],b[N];
struct E{int x,t;E(){}E(int _x,int _t){x=_x,t=_t;}}e[1400000];
inline int sgn(int a,int b){
  int t=(a>>11)*(b&2047)-(b>>11)*(a&2047);
  if(!t)return 0;
  return t<0?-1:1;
}
inline bool cmpf(int a,int b){return sgn(a,b)<0;}
inline bool cmpe(const E&a,const E&b){return sgn(a.x,b.x)<0;}
inline void addrect(const Rect&a,int b,int x){
  l[x][b]=py[++cy]=a.y1<<11|b;
  r[x][b]=py[++cy]=a.y2<<11|b;
  e[++ce]=E(a.x1<<11|b,x<<11|b);
  e[++ce]=E(a.x2<<11|b,(-x)<<11|b);
}
inline int lower(int x){
  int l=1,r=cy,mid;
  while(1){
    if(!sgn(x,py[mid=(l+r)>>1]))return mid;
    if(sgn(x,py[mid])<0)r=mid-1;else l=mid+1;
  }
}
void change(int x,int a,int b){
  if(C<=a&&b<=D){tag[x]+=P;val[x]+=P;return;}
  int mid=(a+b)>>1;
  if(C<=mid)change(x<<1,a,mid);
  if(D>mid)change(x<<1|1,mid+1,b);
  val[x]=max(val[x<<1],val[x<<1|1])+tag[x];
}
void ins(int x,int a,int b){
  has[x]=1;
  if(C<=a&&b<=D){v[++ed]=P;nxt[ed]=g[x];g[x]=ed;return;}
  int mid=(a+b)>>1;
  if(C<=mid)ins(x<<1,a,mid);
  if(D>mid)ins(x<<1|1,mid+1,b);
}
void dfs(int x,int a,int b,int t){
  if(!has[x]||val[x]+t<K)return;
  for(int i=g[x];i;i=nxt[i])if(sgn(del[v[i]],cur)>0)ans[v[i]]=1;
  g[x]=0;
  if(a==b){has[x]=0;return;}
  int mid=(a+b)>>1;
  t+=tag[x];
  dfs(x<<1,a,mid,t),dfs(x<<1|1,mid+1,b,t);
  has[x]=has[x<<1]|has[x<<1|1];
}
void solve(int _K){
  int i,j,o,x,y;
  K=_K;
  for(i=1;i<=n;i++)if(!ans[i]&&f[a[i].y2]>=K)break;
  if(i>n)return;
  ce=cy=0;
  for(i=1;i<=n;i++){
    for(j=K;j>0;j-=2){
      if(j<K&&!ans[i])break;
      addrect(a[i],j,i);
    }
    if(!ans[i])del[i]=a[i].x2<<11|K;
  }
  for(i=1;i<=m;i++)for(j=K-1;j>0;j-=2)addrect(b[i],j,i+n);
  sort(py+1,py+cy+1,cmpf);
  for(_=0,i=1;i<=cy;i++)if(i==1||sgn(py[_],py[i]))py[++_]=py[i];
  cy=_--;
  for(i=1;i<=n;i++)for(j=K;j>0;j-=2){
    if(j<K&&!ans[i])break;
    pl[i][j]=lower(l[i][j]),pr[i][j]=lower(r[i][j])-1;
  }
  for(i=n+1;i<=n+m;i++)for(j=K-1;j>0;j-=2)pl[i][j]=lower(l[i][j]),pr[i][j]=lower(r[i][j])-1;
  sort(e+1,e+ce+1,cmpe);
  ed=0;
  for(j=1;j<=_;j<<=1);j<<=1;
  memset(tag,0,j*sizeof(int));
  memset(val,0,j*sizeof(int));
  memset(g,0,j*sizeof(int));
  memset(has,0,j*sizeof(bool));
  for(i=1;i<=ce;i=j){
    cur=e[i].x;
    for(j=i;j<=ce&&!sgn(cur,e[j].x);j++){
      o=e[j].t;
      x=o>>11;
      y=o&2047;
      if(o>0)o=1;else x=-x,o=-1;
      C=pl[x][y],D=pr[x][y],P=o;
      change(1,1,_);
      if(x<=n&&o==1&&!ans[x]&&y==K)P=x,ins(1,1,_);
    }
    dfs(1,1,_,0);
  }
}
int main(){
  for(f[1]=i=2;i<MX;i++)f[i]=f[i>>1]<<1;
  scanf("%d%d",&n,&m);
  for(i=1;i<=n;i++)scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
  for(i=1;i<=m;i++)scanf("%d%d%d%d",&b[i].x1,&b[i].y1,&b[i].x2,&b[i].y2);
  for(i=2;i<MX;i<<=1)solve(i);
  for(i=1;i<=n;i++)if(ans[i])fin++;
  printf("%d\n",fin);
  for(i=1;i<=n;i++)if(ans[i])printf("%d ",i);
  return 0;
}

  

 

posted @ 2020-01-03 15:12  Claris  阅读(326)  评论(0编辑  收藏  举报