Curing Kingdom
Description
给定无向连通图,每个点有代价。
选择非空点集 S 付费,之后可不断免费“治愈”与已治愈点相邻的点,但要求任意时刻尚未治愈的点集必须连通。
求最小总付费代价。
Solution
Hint1
考虑部分分,原图为一棵树时,答案显然是点权最大的叶子不选,其他叶子都选。
Hint2
对于一般图,因为和连通性有关,考虑点双连通分量。
Hint3
同理,考虑对于缩点后位于叶子的点双,也是点双中最小点权最大的叶子不选,其他叶子都选。
Hint4
对于构造方案,使用双极定向。
四个等价性质:
- 添加无向边 \((S,T)\) 后,图点双连通
- 圆方树中所有方点构成一条链,\(S\) 到 \(T\) 是圆方树的一条直径
- 存在一种对图的边定向的方法,使得 \(S\) 没有入度,\(T\) 没有出度,其他点出入度都非 \(0\)
- 存在一个所有点的排列 \(p\),使得 \(p_1=S,p_n=T\),且任意前缀/任意后缀的导出子图连通
建立一个超级源点 \(S\),向所有开始就选了的节点连边,以那个点权最大的叶子作为 \(T\) 跑双极定向:
双极定向
- 以 \(S\) 为根建立 DFS 树
- 将 \((T,1)\) 加入空队列
- 如果队列非空:
- 取出队首 \((p,d)\)
- 不断跳父亲直到根 \(S\):
- 记当前点为 \(u\),父亲为 \(f\)
- 若该边已经被定向,结束循环
- 如果 \(d=1\) 则 \(f\to u\),否则 \(u\to f\)
- 遍历 \(f\) 的所有前向边 \((f,v)\),若 \(v\) 在 \(u\) 子树内:
- 如果 \(d=1\) 则 \(f\to v\),否则 \(v\to f\)
- 将 \((v,1-d)\) 加入队列
然后拓扑排序得到最终的结果,写的好的话时间复杂度 \(\mathcal O(n+m)\)。
注意特判仅有一个点双连通分量和 \(n=1\) 的情况。
点击查看代码
#include<bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define rep(i,l,r) for(int i=(l); i<=(r); ++i)
#define drep(i,r,l) for(int i=(r); i>=(l); --i)
using namespace std;
const int N=5e5+5;
int _,n,m,t,T,a[N],p[N],c[N],deg[N],fa[N],L[N],R[N];
int dfc,top,cnt,dfn[N],low[N],stk[N];
basic_string<int>E[N],D[N],G[N];
map<int,int>S[N];
inline void chk(int&x,int y) {x=x<y?x:y;}
inline void DFS(int u) {
dfn[u]=low[u]=++dfc,stk[++top]=u;
for(int v:E[u]) {
if(!dfn[v]) {
DFS(v),chk(low[u],low[v]);
if(dfn[u]<=low[v]) {
D[++cnt]+=u,++deg[u];
do {t=stk[top--],D[cnt]+=t,++deg[t];} while(v^t);
}
} else chk(low[u],dfn[v]);
}
}
inline void sol(int u) {
L[u]=++dfc;
for(int v:E[u]) {
if(v==fa[u]) continue;
if(!L[v]) fa[v]=u,sol(v);
else if(L[v]<L[u]) G[v]+=u;
}
R[u]=dfc;
}
inline void sol() {
cin>>n>>m,dfc=top=cnt=T=0;
rep(i,1,n) cin>>a[i];
rep(i,0,n) E[i].clear(),D[i].clear(),G[i].clear(),S[i].clear(),p[i]=c[i]=deg[i]=fa[i]=L[i]=R[i]=dfn[i]=0;
rep(i,1,m) {
int u,v;
cin>>u>>v,E[u]+=v,E[v]+=u;
}
if(n==1) {
cout<<1<<'\n'<<1<<'\n';
return;
}
DFS(1);
rep(i,1,cnt) for(int u:D[i]) if(deg[u]<2&&a[u]<a[p[i]]) p[i]=u;
rep(i,1,cnt) {
for(int u:D[i]) if(deg[u]>1) ++c[i];
if(c[i]<2) T=!T||a[T]<a[p[i]]?p[i]:T;
}
rep(i,1,cnt) if(c[i]<2&&T^p[i]) E[0]+=p[i],E[p[i]]+=0;
if(!E[0].size()) E[0]+=T,E[T]+=0,T=T==1?2:1;
dfc=0,sol(0);
queue<pair<int,int>>q;
q.emplace(T,1);
while(q.size()) {
auto[p,d]=q.front(); q.pop();
for(int u=p; u; u=fa[u]) {
int f=fa[u];
if(S[f].count(u)) break;
S[f][u]=d,S[u][f]=d^1;
for(int v:G[f]) if(!S[f].count(v)&&L[v]<=R[u]&&L[v]>=L[u])
S[f][v]=d,S[v][f]=d^1,q.emplace(v,d^1);
}
}
queue<int>Q;
cout<<E[0].size()<<'\n';
for(int t:E[0]) Q.push(t);
memset(deg,0,n+1<<2);
rep(i,1,n) for(int v:E[i]) if(S[i][v]) ++deg[v];
while(Q.size()) {
int u=Q.front(); Q.pop();
cout<<u<<' ';
for(int v:E[u]) if(S[u][v]) if(!--deg[v]) Q.push(v);
}
cout<<'\n';
}
signed main() {
FASTIO;
cin>>_,a[0]=1e9;
while(_--) sol();
}

浙公网安备 33010602011771号