Legend
Link ( extrm{to Codeforces})。
给定 (n (2 le n le 20000)) 个点的树,以及 (m (1 le m le 10000)) 个人的行走路线(一条简单路径),你需要使得所有人都满意。一个人满意当且仅当:
- ta 的行走路线的所有边上都有一只狗。
- 直接送给 ta 一只狗。
求最少需要多少只狗。输出方案。
Editorial
不妨考虑序列问题怎么做?对于一个人的行走路线,要么给区间全放上狗,要么直接给他一只狗。
人、区间—— (2) 个不同的选择——不禁让人想到了二分图。于是建模很快完成了:
人当作左部图,区间的每一个位置是右部图,源点连人,位置连汇点,边权 (1)。
人向经过的区间的位置一个一个连过去,边权 (infty)。
最后最小割就是答案了。
这个做法也很容易还原到树上,但唯一的不足就是边太多了,达到了 (O(nm)) 级别。
考虑优化建图,序列上可以线段树,树上就可以树链剖分,一下子就降到了 (O(m log^2 n)) 级别!
其实你还可以用倍增的 ( m{ST}) 表优化建图,就变成了 (O(m log n)) 级别啦。
这看起来是个伪·二分图,位置的一侧有 (O(log n)) 层。
于是我们就不妨假设这个做法的复杂度是 (O(sqrt{n log n} m log n)) 啦!
其实还有个问题,怎么输出方案?显然在残余网络上,对于一个人,如果你到不了 ta,那就是你给了 ta 一只狗。
对于一个位置,如果你可以到它,那就是你放了一条狗上去。
Code
今日自闭:写倍增的时候先更新的深度大的(在回溯的时候更新的)。
这么写的话,显然由于上面的还没有被更新,所以下面的更新都没用。。。。
于是倍增数组除了能访问 parent 节点以外,其它位置都是 (0)。TAT。
#include <bits/stdc++.h>
#define __FILE(x)
freopen(#x".in" ,"r" ,stdin);
freopen(#x".out" ,"w" ,stdout)
using namespace std;
int read(){
char k = getchar(); int x = 0;
while(k < '0' || k > '9') k = getchar();
while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
return x;
}
const int MX = 30000 + 23;
const int S = MX * 15 + 1;
const int T = MX * 15 + 2;
const int vMX = MX * 16;
const int INF = 1000000;
int lg2[MX] ,n ,m;
int tr[vMX] ,fl[vMX] ,tot = 1;
struct edge{
int node ,next ,w;
}h[MX * 60];
void addedge(int u ,int v ,int w ,int *head ,int flg = false){
h[++tot] = (edge){v ,head[u] ,w} ,head[u] = tot;
if(flg) addedge(v ,u ,0 ,head ,false);
}
int fa[16][MX] ,dep[MX];
int eid[16][MX] ,ecnt;
void dfs(int x ,int *head){
if(!dep[x]) dep[x] = 1;
// fprintf(stderr ,"dep[%d] = %d
" ,x ,dep[x]);
for(int i = 1 ; i <= 14 ; ++i){
// printf("fa[%d][%d] = %d
" ,i - 1 ,x ,fa[i - 1][x]);
if(!fa[i - 1][fa[i - 1][x]]) break;
fa[i][x] = fa[i - 1][fa[i - 1][x]];
eid[i][x] = ++ecnt;
// printf("E(%d) is for chain (%d ,%d)
" ,ecnt ,x ,fa[i - 1][fa[i - 1][x]]);
addedge(eid[i][x] ,eid[i - 1][x] ,INF ,fl ,1);
addedge(eid[i][x] ,eid[i - 1][fa[i - 1][x]] ,INF ,fl ,1);
}
for(int i = head[x] ,d ; i ; i = h[i].next){
if((d = h[i].node) == fa[0][x]) continue;
fa[0][d] = x ,eid[0][d] = h[i].w;
dep[d] = dep[x] + 1;
dfs(d ,head);
}
}
int LCA(int x ,int y){
if(dep[x] < dep[y]) swap(x ,y);
for(int i = 14 ; ~i ; --i)
if(dep[fa[i][x]] >= dep[y])
x = fa[i][x];
if(x == y) return x;
for(int i = 14 ; ~i ; --i)
if(fa[i][x] != fa[i][y])
x = fa[i][x] ,y = fa[i][y];
return fa[0][x];
}
int jump(int x ,int dist){
for(int i = 14 ; ~i ; --i)
if((dist >> i) & 1)
x = fa[i][x];
return x;
}
void coverchain(int x ,int y ,int id){
if(dep[x] < dep[y]) swap(x ,y);
// fprintf(stderr ,"sp %d %d
" ,x ,y);
// assert(x != y);
int dist = dep[x] - dep[y];
int len = lg2[dist];
// assert(len >= 0);
addedge(id ,eid[len][x] ,INF ,fl ,1);
int tmp = jump(x ,dist - (1 << len));
addedge(id ,eid[len][tmp] ,INF ,fl ,1);
}
void cover(int x ,int y ,int id){
if(dep[x] < dep[y]) swap(x ,y);
int lca = LCA(x ,y);
if(lca == y){
return coverchain(x ,y ,id);
}
coverchain(x ,lca ,id);
coverchain(y ,lca ,id);
}
void init(){
lg2[0] = -1;
for(int i = 1 ; i < MX ; ++i){
lg2[i] = lg2[i - 1] + (i == (i & -i));
}
ecnt = n - 1 + m;
dfs(1 ,tr);
}
int dis[vMX] ,cur[vMX];
bool BFS(int *head){
for(int i = 1 ; i <= ecnt ; ++i){
cur[i] = head[i];
dis[i] = 0;
}
for(auto i : {S ,T}){
cur[i] = head[i];
dis[i] = 0;
}
queue<int> q; q.push(S);
dis[S] = 1;
while(!q.empty()){int x = q.front(); q.pop();
for(int i = head[x] ,d ; i ; i = h[i].next){
if(!dis[d = h[i].node] && h[i].w){
dis[d] = dis[x] + 1;
q.push(d);
}
}
}return dis[T];
}
int DFS(int x ,int limit){
if(x == T || !limit) return limit;
int f ,flow = 0;
for(int i = cur[x] ,d ; i && limit ; i = h[i].next){
cur[x] = i ,d = h[i].node;
if(dis[d] == dis[x] + 1 && (f = DFS(d ,min(limit ,h[i].w)))){
flow += f ,h[i ^ 1].w += f;
h[i].w -= f ,limit -= f;
}
}return flow;
}
void prepareAns(int x ,int *head){
dis[x] = 1;
for(int i = head[x] ,d ; i ; i = h[i].next){
if((dis[d = h[i].node]) || !h[i].w) continue;
prepareAns(d ,head);
}
}
void test(){
while(1){
printf("%d
" ,read());
}
}
int main(){
__FILE(CF786E);
n = read() ,m = read();
for(int i = 1 ,u ,v ; i < n ; ++i){
u = read() ,v = read();
addedge(u ,v ,i ,tr);
addedge(v ,u ,i ,tr);
}
for(int i = 1 ; i < n ; ++i){
addedge(i ,T ,1 ,fl ,true);
}
for(int i = 1 ; i <= m ; ++i){
addedge(S ,i + n - 1 ,1 ,fl ,true);
}
init();
for(int i = 1 ,u ,v ; i <= m ; ++i){
u = read() ,v = read();
cover(u ,v ,i + n - 1);
}
int orgAns = 0;
while(BFS(fl)){
orgAns += DFS(S ,INF);
}
printf("%d
" ,orgAns);
memset(dis ,0 ,sizeof dis);
prepareAns(S ,fl);
int Ans = 0;
for(int i = 1 ; i <= m ; ++i){
if(!dis[i + n - 1]){
++Ans;
}
}
printf("%d " ,Ans);
for(int i = 1 ; i <= m ; ++i){
if(!dis[i + n - 1]){
printf("%d " ,i);
}
}
puts("");
printf("%d " ,orgAns - Ans);
for(int i = 1 ; i < n ; ++i){
if(dis[i]){
printf("%d " ,i);
}
}
puts("");
return 0;
}