题解 [PR #1] 守卫
一眼费用流,第二眼发现费用函数不凸((
考虑一下部分分怎么写:
先把无解判掉
然后 \(w=1\) 的话一定是 \(k\) 个连通块,一共 \(n-k\) 条边
如果 \(|S_i|=1\) 的话:
从这 \(k\) 个点开始跑 prim 拓展即可
正确性考虑最终答案一定在最小生成树上
然后剩下的就不会了 /kk
然而赛时并没有细想怎么判无解的事情
这个东西可能引向正解的思路
首先只有最小生成森林中的边是有用的
现在有一个最小生成森林,可以断开一些边,要求最终每个连通块中恰好有一个守卫
那么一种建图方式是:
每个守卫向其管辖的点连流量为 1 的边
将每个点拆点,限定其流量为 1,出点向其父亲和汇点连流量为 1 的边(根节点只连向汇点)
那么跑最大流,若每个根节点连向汇点的边都满流则有解
一种正解是这个做法的拓展:
现在考虑边带费用:
在同一棵最小生成树中的两个守卫造成的影响是这两个守卫直接一定删一条边
那么每个守卫唯一对应一条到其祖先的边,且每个守卫到其对应边的路径不相交
那么还是刚才的建图方法,每个点的出点向汇点的费用为其向父亲的边的边权
特别的,根节点到汇点的边费用为 \(\inf\)
跑最大费用最大流即可
还有另一种正解:
现在我们已经可以判无解了
考虑贪心在最小生成森林中从大到小删边,若删完仍有解就删的正确性
好像显然
证明的话考虑删去这条边,会形成一个无守卫连通块
考虑这个连通块形成的点集,若最优解中这个点集无守卫,则一定向其它连通块连了一条更小权的边,连上即可
若有守卫,则将一个守卫派到这个连通块,又得到一个无守卫连通块
因为有解,所以重复此过程可以得到一组合法解,这样做即可
跑二分图匹配的过程可以用退流优化,单次 \(O(n^2\sqrt n)\to O(n^2)\)
那么复杂度 \(O(m\log m+n^3)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, k;
bool vis[N];
ll dis[N], sum;
vector<int> st[N], must;
struct tpl{int u, v, w;}g[N];
inline bool operator < (tpl a, tpl b) {return a.w<b.w;}
int head[N], inc[N], back[N], id[N], s, t, ecnt, tot;
struct edge{int to, next, flw; ll cst;}e[N<<1];
inline void add(int s, int t, int f, ll w) {e[++ecnt]={t, head[s], f, w}; head[s]=ecnt;}
namespace graph{
bool vis[N];
int head[N], dsu[N], ecnt;
struct edge{int to, next, val;}e[N<<1];
inline void add(int s, int t, int w) {e[++ecnt]={t, head[s], w}; head[s]=ecnt;}
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
void dfs(int u) {
vis[u]=1; using ::add;
add(u<<1, u<<1|1, 1, 0), add(u<<1|1, u<<1, 0, 0);
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (vis[v]) continue;
add(v<<1|1, u<<1, 1, 0), add(u<<1, v<<1|1, 0, 0);
add(v<<1|1, t, 1, e[i].val), add(t, v<<1|1, 0, -e[i].val), sum+=e[i].val;
dfs(v);
}
}
void kruskal() {
sort(g+1, g+m+1);
memset(head, -1, sizeof(head));
for (int i=1; i<=n; ++i) dsu[i]=i;
for (int i=1,s,t; i<=m; ++i) if ((s=find(g[i].u))!=(t=find(g[i].v))) {
add(g[i].u, g[i].v, g[i].w), add(g[i].v, g[i].u, g[i].w);
dsu[s]=t;
// cout<<"add: "<<g[i].u<<' '<<g[i].v<<endl;
}
tot=n<<1|1; s=++tot; t=++tot;
for (int i=1; i<=n; ++i) if (!vis[i]) ::add(i<<1|1, t, 1, INF), ::add(t, i<<1|1, 0, -INF), must.pb(::ecnt-1), dfs(i), sum+=INF;
}
}
bool spfa(int s, int t) {
memset(dis, -0x3f, sizeof(dis));
memset(back, -1, sizeof(back));
queue<int> q;
inc[s]=INF; dis[s]=0;
q.push(s);
while (q.size()) {
int u=q.front(); q.pop();
// cout<<"u: "<<u<<endl;
vis[u]=0;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (e[i].flw && dis[v]<dis[u]+e[i].cst) {
dis[v]=dis[u]+e[i].cst;
back[v]=i; inc[v]=min(inc[u], e[i].flw);
if (!vis[v]) q.push(v), vis[v]=1;
}
}
}
// cout<<back[t]<<endl;
return ~back[t];
}
pair<ll, ll> dinic(int s, int t) {
ll ans1=0, ans2=0;
while (spfa(s, t)) {
ans1+=inc[t];
ans2+=inc[t]*dis[t];
// cout<<inc[t]<<endl;
// cout<<"dis: "<<dis[t]<<endl;
for (int u=t; u!=s; u=e[back[u]^1].to) {
e[back[u]].flw-=inc[t];
e[back[u]^1].flw+=inc[t];
}
}
return {ans1, ans2};
}
signed main()
{
n=read(); m=read(); k=read();
for (int i=1,u,v,w; i<=m; ++i) {
u=read(); v=read(); w=read();
g[i]={u, v, w};
}
for (int i=1; i<=k; ++i) for (int t=read(); t; --t) st[i].pb(read());
// memset(head, -1, sizeof(head)); ecnt=1;
// for (int i=1; i<=k; ++i) id[i]=n+i;
// s=n+k+1; t=n+k+2;
// for (int i=1; i<=k; ++i) add(s, id[i], 1, 0), add(id[i], s, 0, 0);
// for (int i=1; i<=n; ++i) add(i, t, 1, 0), add(t, i, 0, 0);
// for (int i=1; i<=k; ++i) for (auto it:st[i]) add(id[i], it, 1, 0), add(it, id[i], 0, 0);
// if (dinic(s, t).fir!=k) {puts("-1"); return 0;}
memset(head, -1, sizeof(head)); ecnt=1;
graph::kruskal();
for (int i=1; i<=k; ++i) id[i]=++tot;
for (int i=1; i<=k; ++i) add(s, id[i], 1, 0), add(id[i], s, 0, 0);
for (int i=1; i<=k; ++i) for (auto it:st[i]) add(id[i], it<<1, 1, 0), add(it<<1, id[i], 0, 0);
pair<ll, ll> tem=dinic(s, t);
// cout<<"flw: "<<tem.fir<<endl;
for (auto it:must) if (e[it].flw) {puts("-1"); return 0;}
if (tem.fir!=k) puts("-1");
else printf("%lld\n", sum-tem.sec);
return 0;
}