250. 磁力块

题目链接:250. 磁力块

算法分析

先按照质量排序,然后分块,在每块内再按照距离排序。每块大小 n \sqrt n n 。计算出一个k,满足[1,k]块内的每个磁石的质量都小于等于队头的磁石的磁力。最坏的情况下,会换 n n n次磁石,总共会有 n n n块磁石入队,因此均摊时间复杂度是O(1),暴力计算第 k + 1 k+1 k+1块的时候,复杂度是 O ( n ) O(\sqrt n) O(n )。因此,总复杂度是 O ( n ∗ n ) O(n*\sqrt n) O(nn )

在计算 k k k的时候,可以优化。存下每块内磁石的最大质量,因为之前是排序的,因此这个数据是有序的,可以二分查找出这个k。同样地,在每块内部计算距离小于队头半径的磁石时,也可以二分,但此题时间宽裕,两个二分都不用也能过。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define ll long long 
const int N = 250010;
struct Stone
{
	ll x, y, m, p, r;
	ll bl;
	double dis;
}st[N];
ll n, xx, yy, pl, rl;
int blo, ans;
int vis[N];
struct kuai
{
	ll maxm, l;
}b[550];
queue<Stone> q;
inline ll re()
{
	ll x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9'){if (c == '-') f= -1; c = getchar();}
	while (c >= '0' && c <= '9'){x = x * 10 + c - '0'; c = getchar();}
	return x * f; 
}
bool cmp(Stone a, Stone b)
{
	return a.m < b.m;
}
bool cmp1(Stone a, Stone b)
{
	if (a.bl == b.bl) return a.dis < b.dis;
	else return a.bl < b.bl;
}
bool chk(int mid, int p)
{
	return b[mid].maxm <= p;
}
int szfind(int p)  // 在所有块中查找小于等于p的最大值  
{
	int l = 0, r = st[n].bl;
	while (l < r)
	{
		int mid = (l + r + 1) >> 1;
		if (chk(mid, p)) l = mid; else r = mid - 1;
	}
	return l;
}
int main()
{
	xx = re(); yy = re(); pl = re(); rl = re(); n = re();
	for (int i = 1; i <= n; ++i)
	{
		st[i].x = re(); st[i].y = re(); st[i].m = re(); st[i].p = re(); st[i].r = re();
		st[i].dis = sqrt((st[i].x - xx) * (st[i].x - xx) + (st[i].y - yy) * (st[i].y - yy));
	}
	blo = sqrt(n);
	sort(st + 1, st + n + 1, cmp);  // 按质量由小到大排序  
	for (int i = 1; i <= n; ++i) st[i].bl = (i - 1) / blo + 1;
	for (int i = 1; i <= st[n].bl - 1; ++i)
	{
		b[i].l = (i - 1) * blo +1; // 记录每块的最左边的位置  
		b[i].maxm = st[i*blo].m;  // 记录每块的最大质量  
	}
	b[st[n].bl].l = (st[n].bl - 1) * blo + 1;
	b[st[n].bl].maxm = st[n].m;
	sort(st + 1, st + n + 1, cmp1); // 同一块内按距离由小到大排序  

	Stone tem;
	tem.x = xx; tem.y = yy; tem.p = pl; tem.r = rl;
	q.push(tem);
	while (!q.empty())
	{
		Stone u = q.front(); q.pop();	
		int k = szfind(u.p); // [1,k]块都是最大质量小于等于u.p的 
		for (int t = 1; t <= k; ++t)
		{
			int j;
			for (j = b[t].l; j <= min((ll)t * blo, n); ++j)
				if (st[j].dis <= u.r)
				{
					if (!vis[j])
					{
						++ans; q.push(st[j]); vis[j] = 1;
					}					
				}
				else break;
			b[t].l = j;
		}
		// 暴力计算第k+1个块 
		if (k != st[n].bl)  // 需要判断k是否是最后一个块  
		{
			int j;
			for (j = b[k+1].l; j <= min((ll)(k + 1) * blo, n); ++j)
				if (st[j].dis <= u.r)
				{
					if (st[j].m <= u.p && !vis[j])
					{
						++ans; q.push(st[j]); vis[j] = 1;
					}
				}
				else break;
		}		
	}
	printf("%d\n", ans);
	return 0;
}

总结与反思

  1. 此题细节很多。在二分查找k时,要将左端点 l l l置为0,如果k为0的话,可以正确取值,否则不能正确取值。在暴力计算第 k + 1 k+1 k+1块时,要判断 k k k是否是最后一块,如果是最后一块,就不能在计算第 k + 1 k+1 k+1块了,否则又要错误。光debug这个错误,用了将近一天的时间。

  2. 数据设定为long long了,但是快读的时候忘开long long。

  3. 代码可以再精简的,比如存下 L [ i ] L[i] L[i] R [ i ] R[i] R[i],作为每块的左右端点,更清晰些;在入队列的时候,可以让下标 i i i入队列,不用整个结构体 s t [ i ] st[i] st[i]都入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值