磁力块题解

1.前言

分块好好 v a n van van 啊。

2.题解

link

方法:搜索+分块

由于我们只能在 ( x 0 , y 0 ) (x_0, y_0) (x0,y0) 处吸引磁石,所以我们只需要考虑到 ( x 0 , y 0 ) (x_0, y_0) (x0,y0) 的距离就可以了。所以我们的结构体 N o d e Node Node 保存 4 4 4 个变量 d i s dis dis(到 ( x 0 , y 0 ) (x_0, y_0) (x0,y0)的距离的平方), m m m, p p p, r r r (半径的平方)。attention: 为了防止卡精度,我们可以采用平方的办法,而且也就不需要注意数据类型的转换了。

我们要满足两个要求 B . m ≤ A . p , B . d i s ≤ A . r B.m \leq A.p, B.dis \leq A.r B.mA.p,B.disA.r。 所以我们可以采用 C D Q CDQ CDQ 的思路,想办法先满足一个变量,不妨将 m m m 从小到大排序,分块后每个块按照 d i s dis dis 从小到大排序。

假设我们现在 b f s bfs bfs 搜索到了 t e m tem tem

如果这一个块的 m m m 的最大值小于 t e m . p tem.p tem.p,那么这个块里的所有元素都满足 m m m 的要求,所以只需要再满足 d i s dis dis 的要求就可以了(这就是我们为什么要按照 d i s dis dis 从小到大排序),由于我们的 d i s dis dis 按照从小到大排序,所以如果这个块的两个元素 i , j i, j i,j(排序后, i i i j j j 的前面), 如果 i i i 不满足要求, 那么 j j j 一定不满足要求。所以我们从这个块的第一个元素开始枚举,不满足要求就 b r e a k break break, 满足要求的话就把 t a ta ta 加进队列,并且从这个块中弹出(每个元素只加入队列一次)。

如果这一个块的 m m m 的最大值大于 t e m . p tem.p tem.p,那么之后的块一定不满足要求了。所以我们只需要暴力遍历这个块后,直接 b r e a k break break 掉就可以了。

由于第二种情况选出的元素不方便从块中删除,所以我们用一个 v i s vis vis 数组标记一下 i i i 号元素是否在队列中或者进入过队列,稍微改一改第一种情况的实现就可以了(具体见代码)。

3.参考代码

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define int long long
#define LL long long 
template <typename T>
void read (T &x) {
	x = 0; T f = 1;
	char ch = getchar ();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar ();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + ch - '0';
		ch = getchar ();
	}
	x *= f;
}
template <typename T>
void write (T x) {
	if (x < 0) {
		x = -x;
		putchar ('-');
	}
	if (x < 10) {
		putchar (x + '0');
		return;
	}
	write (x / 10);
	putchar (x % 10 + '0');
}
template <typename T>
void print (T x, char ch) {
	write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }

const int Maxn = 25e4;
const int Maxsn = 500;

int n, sx, sy;

struct Node {
    int dis, m, p, r;
}a[Maxn + 5];
bool cmp_m (Node x, Node y) {
	return x.m < y.m;
}
bool cmp_dis (Node x, Node y) {
	return x.dis < y.dis;
}

int Size, Num;
int l[Maxn + 5], r[Maxn + 5], m_max[Maxn + 5];
#define l(x) ((x - 1) * Size + 1)
#define r(x) (Min ((x) * Size, n))
void Build () {
    sort (a + 1, a + 1 + n, cmp_m);
    Size = sqrt (n); Num = ceil (n * 1.0 / Size);
    for (int i = 1; i <= Num; i++) {
    	l[i] = l (i); r[i] = r (i);
    	for (int j = l[i]; j <= r[i]; j++) 
    		m_max[i] = Max (m_max[i], a[j].m);
    	sort (a + l[i], a + 1 + r[i], cmp_dis);
	}
}
bool check (Node x, Node y) {
	if (y.dis <= x.r && y.m <= x.p) return 1;
	else return 0;
}
//check (x, y) 返回 x 号磁石是否能吸引 y 号磁石 

bool vis[Maxn + 5];

signed main () {
    read (sx); read (sy); read (a[0].p); read (a[0].r); read (n);
    a[0].r = pow (a[0].r, 2);
    for (int i = 1; i <= n; i++) {
        int x, y; read (x); read (y); read (a[i].m); read (a[i].p); read (a[i].r);
        a[i].dis = pow (x - sx, 2) + pow (y - sy, 2);
        a[i].r = pow (a[i].r, 2);
    }
    Build (); 
    
    int ans = 0;
    queue <Node> q; q.push (a[0]);
    while (q.size ()) {
    	Node tem = q.front (); q.pop ();
    	
        for (int i = 1; i <= Num; i++) {
        	if (m_max[i] > tem.p) {//第一不满足的块 
        		for (int j = l[i]; j <= r[i]; j++) {
        			if (vis[j] == 1) continue;
        			if (check (tem, a[j])) {//满足要求,加入队列
        				ans++;
        				vis[j] = 1;
        				q.push (a[j]);
					}
				}
				break;
			}
            while (l[i] <= r[i] && check (tem, a[l[i]])) {//整个块都满足 m 的要求 
            	if (vis[l[i]] == 1) {//进入过队列或在队列中,直接跳过就可以了 
					l[i]++; 
					continue; 
				}
            	ans++; q.push (a[l[i]++]);//满足要求,加入队列 
			}
        }
    }
    write (ans);
	return 0;
}
posted @ 2021-06-22 13:39  C2022lihan  阅读(44)  评论(0编辑  收藏  举报