gym102331f 题解
据说是减半报警器最早出现的地方。
先用一个堆维护所有被激活的边,然后用并查集维护联通块。
对于一条边,如果 \(w_u+w_v\ge s\),则有 \(\max(w_u,w_v)\ge\lceil\dfrac s2\rceil\)。对每个连通块维护一个堆记录一端在这个连通块里的边的阈值即可。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,w[300005],fa[300005];
struct edge{
int u,v,w;
}e[300005];
int tog,las[300005],stp[300005];
struct node{
int val,id,tim;
bool operator<(const node &t)const{return val>t.val;}
};
priority_queue<node>q[300005];
int find(int x){
if(fa[x]==x)return fa[x];
else return fa[x]=find(fa[x]);
}
priority_queue<int,vector<int>,greater<int> >qq;
void Sol(node tmp){
int p=tmp.id;
if(find(e[p].u)==find(e[p].v))return;
int sum=w[find(e[p].u)]+w[find(e[p].v)];
if(stp[p]+e[p].w<=sum){
qq.push(p);
las[p]=-1;
return;
}
e[p].w=stp[p]+e[p].w-sum;
stp[p]=sum;
int l=(e[p].w+1)/2;
las[p]=++tog;
q[find(e[p].u)].push(node({w[find(e[p].u)]+l,p,tog}));
q[find(e[p].v)].push(node({w[find(e[p].v)]+l,p,tog}));
}
void ch(int id){
while(!q[id].empty()&&q[id].top().val<=w[id]){
node tmp=q[id].top();
q[id].pop();
if(tmp.tim!=las[tmp.id])continue;
Sol(tmp);
}
}
vector<int>ans;
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&e[i].u,&e[i].v,&e[i].w);
if(w[e[i].u]+w[e[i].v]>=e[i].w)qq.push(i),las[i]=-1;
else{
las[i]=++tog;
stp[i]=w[e[i].v]+w[e[i].u];
e[i].w-=w[e[i].v]+w[e[i].u];
int l=(e[i].w+1)/2;
q[e[i].u].push(node({w[e[i].u]+l,i,tog}));
q[e[i].v].push(node({w[e[i].v]+l,i,tog}));
}
}
for(int i=1;i<=n;i++)fa[i]=i;
while(!qq.empty()){
int tmp=qq.top();
qq.pop();
int c=find(e[tmp].u),d=find(e[tmp].v);
if(c==d)continue;
ans.push_back(tmp);
if(q[c].size()>q[d].size())swap(c,d);
fa[c]=d;
w[d]+=w[c];
while(!q[c].empty()){
node tmp=q[c].top();
q[c].pop();
if(tmp.tim!=las[tmp.id])continue;
q[d].push(tmp);
}
ch(d);
}
printf("%lld\n",(int)ans.size());
for(int x:ans)printf("%lld ",x);
return 0;
}