zoukankan      html  css  js  c++  java
  • 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的常数不同.

  • 相关阅读:
    java内部类
    重新回顾JSP
    vs 链接动态库 (不用放在可执行文件同一目录)
    c++ 文件夹读取文件
    为人处世
    Windows常用软件
    windows好用的软件
    冒泡排序,快速排序,归并排序
    最大公约数、最小公倍数、所有约数
    linux U盘 硬盘 unable to mount
  • 原文地址:https://www.cnblogs.com/zsyzlzy/p/12373899.html
Copyright © 2011-2022 走看看