浙江农林大学第二十二届程序设计竞赛(同步)
题意:
- 每次攻击有传导性(连锁劈人),求用最少的次数劈死所有人,在线询问,询问独立。
- 和最近蓝桥杯排雷那题很像
思路:
-
观察到特殊的范围,传递攻击距离不大。数据范围小不是无端的,往往通常蕴藏了出题人想考察的算法。
-
敌人在坐标系中,显然需要离散化处理,同时也得优化查找、合并人的过程(连锁劈人类似合并)。STL 自动离散化,二分搜索,很适合处理此题。
C o d e : Code: Code:
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
#define all(a) a.begin(),a.end()
#define oper(a) (operator<(const a& ee)const)
#define endl "\n"
#define ul (u << 1)
#define ur (u << 1 | 1)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
double DNF = 1e17;
const int N = 100010, M = 200010, MM = 110;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
int n, m, k, T, S, D, K;
struct node
{
ll x, y; int id;
bool oper(node) { return x < ee.x; }
};
int p[N];
int find(int x) {
return p[x] == x ? p[x] : p[x] = find(p[x]);
}
ll dist(ll x1, ll y1, ll x2, ll y2) {
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
}
int res = 0;
void unite(int a, int b) {
a = find(a), b = find(b);
if (a ^ b) p[a] = b, res--;//如果能连通就减操作次数
}
void solve() {
cin >> D >> n;
forr(i, 1, n)p[i] = i;
set<node> se;//因为保证了横坐标均不同,我们可以把所有敌人的横坐标放入一个set,当然也要顺带 y
forr(i, 1, n) {
ll x, y;
cin >> x >> y;
res++;//假设此人需要一次闪电
auto t = se.lower_bound({ x - D, 0, 0 });
//d 的小范围在此刻的作用就体现出来了
//我们二分查找所有在 [x - D,x + D] 范围内的敌人,超出这范围的敌人显然不会被连锁
//而 x 坐标均不同也能保证遍历次数的上限是 2 * D
while (t != se.end())
{
auto [sx, sy, id] = *t;
if (sx > x + D)break;//超出范围
if (dist(x, y, sx, sy) <= D * D)//这里要注意精度问题,不要sqrt
unite(i, id);
++t;
}
se.insert({ x,y,i });//在线维护
cout << res << endl;
}
}
int main() {
cinios;
T = 1;
while (T--)solve();
return 0;
}
/*
*/