【题目】
原题地址
给
盏灯,每盏灯能照到的角度范围是相同的,第
盏灯在第
秒或者被
盏灯照到后都会亮起,问所有灯都在什么时刻亮起。
【题目分析】
看上去很玄学,实际上重要的点在思路的转化。我最喜欢分治了。
我最喜欢薇尔莉特!!
【解题思路】
首先第一眼看上去像计算几何qwq,不过因为所有灯朝向一样,所以我们转一下坐标轴就行了。
我们把
变成
使得
然后可以得到:
这些坐标分母一样,所以可以丢掉。
转化问题后,我们发现这就是就是按顺序查询一个点左下角的区域内第
小的数是多少(记为
)。
然后有
,特别地,如果没有
个灯泡,
。
考虑怎么维护这个东西?
我们可以写树套树维护一下插入操作,以往都是要二分然后再查询的,这样时间复杂度是
的,显然不可以接受….
但是这题只需要查
大,所以我们可以反过来,以
为第一关键字建线段树,然后对
坐标建平衡树,这样就可以省去二分了。 想用带修主席树?不好意思卡内存呢。
查询操作我们就可以二分答案了。
具体做法就是从线段树根结点开始,先看左儿子所套的平衡树里边
坐标小于等于
的结点个数。
设有
个,如果
,说明答案应该在
之间,然后递归处理。
否则答案就应该在
之间,然后递归处理。
直到
的时候,便可确定
。
查询操作中,我们要访问
个线段树结点,在每个线段树节点中,都要
地查找相关点的个数。
所以查询操作的时间复杂度也是
的。
然后有
个灯泡,所以总时间复杂度是
的。
每个灯泡的信息会被插入到
个线段树所套的平衡树里,
所以空间复杂度是
的。
以上,太麻烦了,我不想写。对于我来说,正确的操作姿势应该是整体二分。
直接二分时间
同时
为在此段时间内点亮的灯,取
,
将
划分为
与
表示在
前点亮的灯与在Mid后点亮的灯,递归求解即可。
那么现在问题是如何求一盏灯是否在
前点亮,关键在于求一盏灯的左下角的灯的数目。
我们直接用扫描线做就行了:将灯按照
坐标排序,用树状数组维护
坐标,横向依次扫描就可以完成了。
如果求得的左下角的灯的个数大于等于
个,或者灯的编号大于等于
,则把它归到
的区间。
然后就是这样,写分治不仅代码简单,而且常数小,何乐而不为呢? (AC时速度第4,代码2kb,写数据结构的话速度和代码量都是两倍左右)
~~强烈鄙视这种文末一定要带换行符的PE!!!~~
【参考代码】
整体二分
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF=2e9;
const int N=2e5+10;
int n,tx,ty,txx,tyy;
LL X[N],Y[N],dat[N];
struct Tnode
{
int x,y,k,ans,id;
bool operator < (const Tnode &A)const
{
if(x==A.x)
return y<A.y;
return x<A.x;
}
};
Tnode a[N];
struct Tbit
{
int tr[N];
int lowbit(int x){
return x&(-x);
}
void add(int x,int v)
{
for(;x<=n;x+=lowbit(x))
tr[x]+=v;
}
int query(int x)
{
int ret=0;
for(;x;x-=lowbit(x))
ret+=tr[x];
return ret;
}
};
Tbit BIT;
bool cmp(Tnode A,Tnode B)
{
return A.id<B.id;
}
void update(int &x,int &y)
{
int mx=max(abs(x),abs(y));
int d=((LL)INF+mx-1)/mx;
x*=d;y*=d;
++x;
}
void init()
{
for(int i=1;i<=n;++i)
dat[i]=X[i];
sort(dat+1,dat+n+1);
int sz=unique(dat+1,dat+n+1)-dat;
for(int i=1;i<=n;++i)
a[i].x=lower_bound(dat+1,dat+sz+1,X[i])-dat;
for(int i=1;i<=n;++i)
dat[i]=Y[i];
sort(dat+1,dat+n+1);
sz=unique(dat+1,dat+n+1)-dat;
for(int i=1;i<=n;++i)
a[i].y=lower_bound(dat+1,dat+sz+1,Y[i])-dat;
}
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 res=BIT.query(a[i].y);
if(a[i].id<=Mid || res>=a[i].k)
{
BIT.add(a[i].y,1);
++mid;swap(a[mid],a[i]);
}
else
a[i].k-=res;
}
for(int i=l;i<=mid;++i)
BIT.add(a[i].y,-1);
if(l<=mid)
solve(L,Mid,l,mid);
if(r>mid)
solve(Mid+1,R,mid+1,r);
}
int main()
{
// freopen("BZOJ3833.in","r",stdin);
// freopen("BZOJ3833.out","w",stdout);
scanf("%d%d%d%d%d",&n,&tx,&ty,&txx,&tyy);
if(1ll*tx*tyy==1ll*ty*txx)
update(tx,ty);
if(1ll*tx*tyy<1ll*ty*txx)
swap(tx,txx),swap(ty,tyy);
for(int i=1;i<=n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
a[i].id=i;
X[i]=1ll*x*tyy-1ll*txx*y;
Y[i]=1ll*tx*y-1ll*x*ty;
}
for(int i=1;i<=n;++i)
scanf("%d",&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;
}
然后树套树的没写,在这里放一份大佬的代码qwq。来源
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
//#include<ctime>
#define N 200010
using namespace std;
struct ppp
{
long long x,y;
int num,k;
}b[N];
bool cmp(ppp x,ppp y)
{
if(x.x!=y.x) return x.x<y.x;
return x.y<y.y;
}
long long ax[N],ay[N];
map<long long,int>px,py;
int ans[N];
int nx,ny;
int l[N<<2],r[N<<2],rt[N<<2];
int ch[N*20][2],siz[N*20],a[N*20],fa[N*20],ti[N*20],cnt;
inline void pup(int x){siz[x]=siz[ch[x][0]]+ti[x]+siz[ch[x][1]];}
void rotate(int x,int &k)
{
int y=fa[x],z=fa[y];
int l=0,r;
if(ch[y][1]==x) l=1;r=l^1;
if(y==k) k=x;
else
{
if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
}
fa[ch[x][r]]=y;
fa[y]=x;
fa[x]=z;
ch[y][l]=ch[x][r];
ch[x][r]=y;
pup(y);//pup(x);
}
void splay(int x,int &k)
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if(ch[z][0]==y^ch[y][0]==x) rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
}
void add(int &k,int x)
{
if(!k)
{
cnt++;k=cnt;
siz[cnt]=ti[cnt]=1;
a[cnt]=x;
return;
}
int z,p=k;
while(p)
{
z=p;
siz[z]++;
if(x==a[z])
{
ti[z]++;
return;
}
if(x>a[z])
p=ch[z][1];
else p=ch[z][0];
}
ti[++cnt]=1;
siz[cnt]=1;
a[cnt]=x;
fa[cnt]=z;
if(x>a[z])
ch[z][1]=cnt;
else ch[z][0]=cnt;
splay(cnt,k);
}
int numgetrank(int k,int x,bool f)
{
if(!k) return 0;
if(a[k]==x)
{
if(!f) return siz[ch[k][0]]+ti[k];
return ti[k];
}
if(x<a[k]) return numgetrank(ch[k][0],x,f);
else
{
if(!f) return numgetrank(ch[k][1],x,f)+siz[ch[k][0]]+ti[k];
return numgetrank(ch[k][1],x,f);
}
}
inline void build(int now,int ll,int rr)
{
l[now]=ll;r[now]=rr;
if(ll==rr) return;
int mid=(ll+rr)>>1;
build(now<<1,ll,mid);
build(now<<1|1,mid+1,rr);
}
inline int check(int now,int v,int k,bool f)
{
if(l[now]==r[now]) return l[now];
int t=numgetrank(rt[now<<1],v,f);
if(k<=t) return check(now<<1,v,k,f);
else return check(now<<1|1,v,k-t,f);
}
inline void change(int now,int x,int v)
{
add(rt[now],v);
if(l[now]==r[now]) return;
int mid=(l[now]+r[now])>>1;
if(x<=mid) change(now<<1,x,v);
else change(now<<1|1,x,v);
}
const int ___=1<<15;
char ZZ[___|1],*_=ZZ,*Z=ZZ,Z_[___],_Z;
#define getc() (_==Z&&(Z=(_=ZZ)+fread(ZZ,1,___,stdin),_==Z)?0:*_++)
inline int read()
{
int x=0,f=1;
char c=getc();
for(;c<48||c>57;c=getc())if(c==45)f=-1;
for(;c>=48&&c<=57;)x=x*10+c-48,c=getc();
return x*f;
}
inline long long readll()
{
long long x=0,f=1;
char c=getc();
for(;c<48||c>57;c=getc())if(c==45)f=-1;
for(;c>=48&&c<=57;)x=x*10+c-48,c=getc();
return x*f;
}
long long X1,X2,Y1,Y2;
int main()
{
int n;
int tot=0,st=clock();
n=read();
long long x,y;
X1=readll();Y1=readll();X2=readll();Y2=readll();
bool fl=false;
if(X1*Y2==Y1*X2)
{
fl=true;
X2=-Y1;Y2=X1;
}
long long tmp=1;
if(X1*Y2-Y1*X2<0) tmp=-1;
int i,j;
for(i=1;i<=n;i++)
{
x=readll();y=readll();
b[i].x=tmp*(x*Y2-y*X2);
b[i].y=tmp*(y*X1-x*Y1);
b[i].num=i;
ax[i]=b[i].x;
ay[i]=b[i].y;
}
for(i=1;i<=n;i++)
b[i].k=read();
sort(ax+1,ax+n+1);
ax[0]=707185547;
for(i=1;i<=n;i++)
if(ax[i]!=ax[i-1])
{
nx++;
px[ax[i]]=nx;
}
sort(ay+1,ay+n+1);
ay[0]=707185547;
for(i=1;i<=n;i++)
if(ay[i]!=ay[i-1])
{
ny++;
py[ay[i]]=ny;
}
sort(ax+1,ax+n+1);
sort(ay+1,ay+n+1);
int sx=unique(ax+1,ax+n+1)-ax;
int sy=unique(ay+1,ay+n+1)-ay;
for (int i = 1; i <= n; i ++)
{
int u=lower_bound(ax+1,ax+sx,b[i].x)-ax;
int v=lower_bound(ay+1,ay+sy,b[i].y)-ay;
b[i].x=u,b[i].y=v;
}
sort(b+1,b+n+1,cmp);
build(1,1,n);
for(i=1;i<=n;i++)
{
tmp=numgetrank(rt[1],b[i].y,fl);
if(tmp<b[i].k) ans[b[i].num]=b[i].num;
else ans[b[i].num]=min(check(1,b[i].y,b[i].k,fl),b[i].num);
// st=clock();
change(1,ans[b[i].num],b[i].y);
// tot+=clock()-st;
}
for(i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d",ans[n]);
// cout<<tot;
}
【总结】
题目的转化很巧妙(其实也还好)。
主要是这个坐标系的转化,牢记在心。
为了薇尔莉特!