caioj 2062& CH 0x40数据结构进阶(0x44 分块)例题3:磁力块

传送门
这题看完后一头雾水,看完题解后豁然开朗.

题目要我们一个一个吸,我们才不要听它的 ,直接使用大功率吸引术 把一块磁铁能吸的全部吸过来,之后这块磁铁就没用了.

按照这个思路,我们可以用bfs,一次把队头可吸的全吸来,再把队头出队.

现在,我们只需考虑怎么吸吸得快了.
本题中,磁石吸引的条件为:质量le磁力,距离le半径.
不妨先按质量排序吧.
排序完后,那么一定存在一个整数k,满足:

  1. 第1~k个磁铁的质量le磁力.
  2. 第k+1~n个磁铁的质量>磁力.

因为质量有序了嘛.(k可以为0,表示吸不到其他磁铁)

但这样以后,距离毫无规律,我们能不能又让局部的距离有序呢?
当然可以!这样一想,我们试试用分块做.
设一共分成tt大块.我们在之前的基础上,对每一个大块按距离排序.

那么又一定存在一个整数kk,满足:

  1. 第1~kk-1大块的磁铁的质量le磁力.
  2. 第kk+1~t大块的磁铁的质量>磁力.

为什么?其实kk就在k所在的大块中.
如图:注:点的纵坐标表示磁石的质量.
在这里插入图片描述
k前面的磁石的质量都不大于k的,k后面的磁石的质量都不小于k的.
所以前(后)面大块的磁石无论怎么排序,质量都小(大)于等于k的质量.

因为我们对每一大块都按距离排了序,所以kk前面的块只有前部合法.(我们照样能找到一个分界点),我们可以把这块的开头部分移到那个分界点以后,以保证每个点只入一次队.
对于kk块的点,全部扫一遍就行.

时间复杂度证明:

每个点至多进队一次,O(n)O(n)
每次kk前的块,都要询问一下开头是否合法.O(nt)()O(n*t)(进队数*块的总数)
每次暴力询问kk块中的磁石能否入队,O(nn/t)()O(n*n/t)(进队数 * 块的长度)
O(n)O(n)忽略,则复杂度为O(n(t+n/t))O(n*(t+n/t))
虽然复杂度与运行时 间没有线性关系(不成正比),但是我们还是希望复杂度越小越好.
t=n/tt=n/t时,复杂度最小,t=sqrt(n)t=sqrt(n)

所以时间复杂度为O(nn)O(n *sqrt{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
",r);
	return 0;
}

尾声

其实CH给的数据让先全部按距离sort的代码跑得更快,应该是sort的常数不同.

原文地址:https://www.cnblogs.com/zsyzlzy/p/12373899.html