CF786E ALT
题意
有一棵 \(n\) 个点的数,和 \(m\) 个人,每个人的路径是树上的一条链。
每个人都喜欢巴啦啦小魔仙。你需要给出一些魔法棒(给人或给树上节点)。
每个人希望要么自己得到魔法棒,要么自己的路径每个点都有魔法棒。
问满足每个人要求的最小魔法棒数,需要输出方案。
题解
我们发现这个题并不可以用数据结构做,也没有奇妙的算法。
考虑暴力一点,网络流。(\(n=2\times10^4\) 不是问题,我甚至在 \(\tt 550ms\) 内跑过 \(n=2\times10^5\) 的问题)。
我们思考建边,用流量代表魔法棒,然后是一个最小割(为了方便,我仅把边权分成 \(1\) 和 \(\infty\) 两种)。
- 对于每个人,从源点连向他,流量为 \(1\)(这是给人的选择)。
- 对于每个人,从他连向他的路径上的每个点,流量为 \(\infty\)(这是给节点的选择)。
考虑最小割的过程:
- 要么选择割掉 1 类边,这样这个人本身已经分割完毕
- 要么选择割掉 2 类边,这时需要所有的 2 类边都被割开才能完成分割。
所以最小割,即最大流就是答案。
输出方案:在跑完最大流的网络上 \(\tt dfs\)。
完了?
发现最多有 \(n^2\) 条边。即使 \(\tt dinic\) 复杂度能承受,边数也承受不了。
但是这是 一个点向一堆连续的点连边,考虑线段树优化。(有一说一这场的 \(\tt B\) 是建图优化,还是板)
但是这是树链不是区间,所以需要树剖,然后再线段树。
总边数 \(n\log^2n\),问题不大。
事实是以前的最大流板子有锅,然后浪费了 \(\texttt{1h+}\) 去调。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
const int N = 2e4 + 5;
const int S = N * 5 + 5;
const int inf = 1e9;
int n,m,st,ed,res;
int id[N << 2];
vector<pair<int,int>> G[N];
int dep[N],siz[N],son[N],fa[N],dfn[N],adfn[N],num[N],tot = 0,top[N];
namespace Dinic{
struct edge{ int v,w; edge *nxt,*inv; bool flg; } *from[S];
void adde(int u,int v,int w){
edge *E = from[u] = new(edge){v,w,from[u],nullptr,1};
edge *F = from[v] = new(edge){u,0,from[v],nullptr,0};
E->inv = F; F->inv = E;
}
queue<int> q;
int dep[S];
edge *now[S];
bool vis[S];
int level(){
while(!q.empty()) q.pop();
q.push(st);
memset(dep,0,sizeof dep);
memcpy(now,from,sizeof now);
dep[st] = 1;
while(!q.empty()){
int u = q.front(); q.pop();
for(edge *e = from[u];e;e = e->nxt){
int v = e->v,w = e->w;
if(w && !dep[v]){
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[ed];
}
int dfs(int u,int fl){
if(u == ed) return res += fl,fl;
int cur = fl;
for(edge *e = now[u];e && cur;e = e->nxt){
now[u] = e;
int v = e->v,w = e->w;
if(w && dep[v] == dep[u] + 1){
int cfl = dfs(v,w < cur ? w : cur);
e->w -= cfl; e->inv->w += cfl; cur -= cfl;
}
}
return fl - cur;
}
void solve(){ while(level()) dfs(st,inf); }
void fnd(int u){
vis[u] = true;
for(edge *e = from[u];e;e = e->nxt){
int v = e->v,w = e->w;
if(w && !vis[v]) fnd(v);
}
}
void output(){
fnd(st);
int cnt0 = 0,cnt1 = 0;
for(edge *e = from[st];e;e = e->nxt)
if(!vis[e->v]) ++cnt0;
printf("%d ",cnt0);
for(edge *e = from[st];e;e = e->nxt)
if(!vis[e->v]) printf("%d ",e->v - 4 * n);
for(edge *e = from[ed];e;e = e->nxt)
if(vis[e->v]) ++cnt1;
printf("\n%d ",cnt1);
for(edge *e = from[ed];e;e = e->nxt)
if(vis[e->v]) printf("%d ",num[adfn[id[e->v]]]);
}
} using Dinic::adde;
#define lc (i << 1)
#define rc (i << 1 | 1)
#define mid ((L + R) >> 1)
#define ls lc,L,mid
#define rs rc,mid + 1,R
#define ID int i = 1,int L = 1,int R = n
void build(ID){
if(L == R) return id[i] = L,adde(i,ed,1);
adde(i,lc,inf); adde(i,rc,inf);
build(ls); build(rs);
}
void link(int k,int l,int r,ID){
if(l <= L && R <= r) return adde(k,i,inf);
if(l <= mid) link(k,l,r,ls);
if(r > mid) link(k,l,r,rs);
}
void dfs1(int u = 1,int ft = 0){
dep[u] = dep[fa[u] = ft] + 1;
siz[u] = 1;
for(auto[v,i] : G[u]) if(v != ft){
num[v] = i;
dfs1(v,u);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u = 1,int tp = 1){
adfn[dfn[u] = ++tot] = u; top[u] = tp;
if(son[u]) dfs2(son[u],tp);
for(auto[v,i] : G[u]) if(!top[v]) dfs2(v,v);
}
void lnk(int k,int x,int y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x,y);
link(k,dfn[top[x]],dfn[x]);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
if(x != y) link(k,dfn[x] + 1,dfn[y]);
}
int main(){
scanf("%d%d",&n,&m);
rep(i,1,n - 1){
int u,v;
scanf("%d%d",&u,&v);
G[u].emplace_back(v,i);
G[v].emplace_back(u,i);
}
dfs1(); dfs2();
st = 4 * n + m + 1,ed = 4 * n + m + 2;
build();
for(int i = 1;i <= m;++i){
int x,y,I = 4 * n + i;
scanf("%d%d",&x,&y);
lnk(I,x,y);
adde(st,I,1);
}
Dinic::solve();
printf("%d\n",res);
Dinic::output();
return 0;
}

浙公网安备 33010602011771号