传送门
这题看完后一头雾水,看完题解后豁然开朗.
题目要我们一个一个吸,我们才不要听它的 ,直接使用大功率吸引术 把一块磁铁能吸的全部吸过来,之后这块磁铁就没用了.
按照这个思路,我们可以用bfs,一次把队头可吸的全吸来,再把队头出队.
现在,我们只需考虑怎么吸吸得快了.
本题中,磁石吸引的条件为:质量
≤
\le
≤磁力,距离
≤
\le
≤半径.
不妨先按质量排序吧.
排序完后,那么一定存在一个整数k,满足:
- 第1~k个磁铁的质量 ≤ \le ≤磁力.
- 第k+1~n个磁铁的质量>磁力.
因为质量有序了嘛.(k可以为0,表示吸不到其他磁铁)
但这样以后,距离毫无规律,我们能不能又让局部的距离有序呢?
当然可以!这样一想,我们试试用分块做.
设一共分成
t
t
t大块.我们在之前的基础上,对每一个大块按距离排序.
那么又一定存在一个整数kk,满足:
- 第1~kk-1大块的磁铁的质量 ≤ \le ≤磁力.
- 第kk+1~t大块的磁铁的质量>磁力.
为什么?其实kk就在k所在的大块中.
如图:注:点的纵坐标表示磁石的质量.
k前面的磁石的质量都不大于k的,k后面的磁石的质量都不小于k的.
所以前(后)面大块的磁石无论怎么排序,质量都小(大)于等于k的质量.
因为我们对每一大块都按距离排了序,所以kk前面的块只有前部合法.(我们照样能找到一个分界点),我们可以把这块的开头部分移到那个分界点以后,以保证每个点只入一次队.
对于kk块的点,全部扫一遍就行.
时间复杂度证明:
每个点至多进队一次,
O
(
n
)
O(n)
O(n)
每次kk前的块,都要询问一下开头是否合法.
O
(
n
∗
t
)
(
进
队
数
∗
块
的
总
数
)
O(n*t)(进队数*块的总数)
O(n∗t)(进队数∗块的总数)
每次暴力询问kk块中的磁石能否入队,
O
(
n
∗
n
/
t
)
(
进
队
数
∗
块
的
长
度
)
O(n*n/t)(进队数 * 块的长度)
O(n∗n/t)(进队数∗块的长度)
O
(
n
)
O(n)
O(n)忽略,则复杂度为
O
(
n
∗
(
t
+
n
/
t
)
)
O(n*(t+n/t))
O(n∗(t+n/t))
虽然复杂度与运行时 间没有线性关系(不成正比),但是我们还是希望复杂度越小越好.
当
t
=
n
/
t
t=n/t
t=n/t时,复杂度最小,
t
=
s
q
r
t
(
n
)
t=sqrt(n)
t=sqrt(n)
所以时间复杂度为 O ( n ∗ n ) O(n *\sqrt{n}) O(n∗n)
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define g getchar()
#define y0 ___
#define x0 __
//x0,y0在cmath中有,会出现变量冲突.
using namespace std;
typedef long long ll;
const int N=250010,T=510;
struct rec{int m,p; ll d,r;}a[N];
int n,x0,y0,t,len,L[T],R[T],M[T],q[N],l,r;
bool v[N];
bool cmp_m(rec a,rec b){return a.m<b.m;}//按质量sort
bool cmp_d(rec a,rec b){return a.d<b.d;}//按距离sort——为了保证精度,所以距离全部平方
template<class o>
void qr(o&x)
{
char c=g;bool v=(x=0);
while(!( ('0'<=c&&c<='9') || c=='-' ))c=g;
if(c=='-')v=1,c=g;
while('0'<=c&&c<='9')x=x*10+c-'0',c=g;
if(v)x=-x;
}
int main()
{
qr(x0);qr(y0);qr(a[0].p);qr(a[0].r);qr(n);
a[0].r*=a[0].r;
for(int i=1;i<=n;i++)
{
ll x,y;
qr(x);qr(y);qr(a[i].m);qr(a[i].p);qr(a[i].r);
a[i].r*=a[i].r;
a[i].d=(x-x0)*(x-x0)+(y-y0)*(y-y0);
}
sort(a+1,a+n+1,cmp_m);
t=sqrt(n);
if(t)len=n/t;
for(int i=1;i<=t;i++)
{
L[i]=R[i-1]+1;R[i]=i*len;M[i]=a[R[i]].m;//记录每段的质量最大值.
sort(a+L[i],a+R[i]+1,cmp_d);
}
if(R[t]<n)
{
t++;
L[t]=R[t-1]+1;R[t]=n;M[t]=a[n].m;
sort(a+L[t],a+n+1,cmp_d);
}
l=r=0;q[0]=0;
while(l<=r)
{
ll rad=a[q[l]].r;int p=a[q[l]].p;l++;
for(int i=1;i<=t;i++)
{
if(M[i]>p)
{
for(int j=L[i];j<=R[i];j++)
if(!v[j]&&a[j].m<=p&&a[j].d<=rad)
v[q[++r]=j]=1;
break;
}
while(L[i]<=R[i]&&a[L[i]].d<=rad)
{
if(!v[L[i]])q[++r]=L[i];
L[i]++;
}
}
}
printf("%d\n",r);
return 0;
}
尾声
其实CH给的数据让先全部按距离sort的代码跑得更快,应该是sort的常数不同.