题解:P12845 [蓝桥杯 2025 国 A] 连锁反应【数据强度待检验】
P12845 [蓝桥杯 2025 国 A ] 连锁反应
题目分析
分析题目可知,爆炸是一种树形结构,我们要引爆的是跟节点,那现在要求的就是有多少棵树
问题转化
现在的问题就是计算一个节点所在的最大树并把它清除 然后从左到右遍历出所有树
思路
这里提供一个贪心算法
- 根节点为炸毁当前连通块已遍历节点的最右节点(因为我们从左往右遍历)
- 那怎么更新呢这个跟节点呢? 如果一个节点能炸毁当前根节点,那这个节点一定能炸毁连通块已遍历节点,因为根节点会继续引爆
- 如果一个节点不能炸毁当前根节点,那他一顶定炸不毁当前根节点(能炸毁且靠右的点会被更新为根节点,说明右边没有点能炸毁当前根节点),那自然炸不掉,不会更优
解法
那我们只需要维护右最大炸毁边界和最优根节点,
O(n) 遍历即可,排序时间复杂度O(nlog{n}) ,这个算法即便l, r 达到1e18 也是可以的,数据适配范围上优于直接使用SCC 。代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
struct sg{
int x,l,r;
};
bool cmp(sg a,sg b){
return a.x < b.x;
}
bool cmp1(sg a,sg b){
return a.l < b.l;
}
int sm[200005];
void solve(){
int n; cin >> n;
vector<sg> a(n);
for (int i = 0; i < n; i++){
int p,l,y;
cin >> p >> l >> y;
a[i] = {p,p-l,p+y};
}
sort(a.begin(),a.end(),cmp);
queue<sg> q;
for (int i = 0; i < n; i++){
q.push(a[i]);
}
int ans = 0,x=-1e18;
sort(a.begin(),a.end(),cmp1);
queue<sg> q1;
for (int i = 0; i < n; i++){
q1.push(a[i]);
}
int r=-1e18;
while(!q.empty()){
if(q1.front().l > x){
x=q.front().x;
q.pop();
ans++;
}
while(!q1.empty() && q1.front().l <= x){
r=max(r,q1.front().r);
x=max(x,q1.front().x);
q1.pop();
}
while(!q.empty() && q.front().x <= r){
if(q.front().l <= x&& q.front(). x){
x=max(x,q.front().x);
}
r=max(r,q.front().r);
q.pop();
}
}
cout << ans << endl;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t = 1;
//cin >> t;
while (t--) solve();
return 0;
}