自闭选手又做不出构造题了,关键是没想到把两条路径合并成一条。
先猜测结论,答案可以达到∑i=1N−1min(2,ci)\sum_{i=1}^{N-1} \min(2,c_i)∑i=1N−1min(2,ci)的上界,其中cic_ici是经过第iii条边的路径数目。
考虑每次缩掉一个度为111的节点uuu的构造。
设跟uuu直接相连的边为iii。若ci=0c_i=0ci=0可以直接缩,ci=1c_i=1ci=1的话那条边怎么定向没有区别,可以将它的端点缩到跟uuu相连的点,ci≥2c_i\geq 2ci≥2的话,我们取两条经过iii的路径(u,x)(u,x)(u,x)和(u,y)(u,y)(u,y),我们可以可以将它们替换为一条路径(x,y)(x,y)(x,y),若最终定向为xxx->yyy,我们令xxx->uuu,uuu->yyy,否则令yyy->uuu,uuu->xxx。这样做的话,对于(x,y)(x,y)(x,y)路径上的边覆盖次数不变,对于(u,x)∩(u,y)(u,x)\cap(u,y)(u,x)∩(u,y)上的边一定达到上界,因此显然不会改变答案。
时间复杂度O(NM)\mathcal O(NM)O(NM)。
#include <bits/stdc++.h>
#define FR first
#define SE second
using namespace std;
typedef pair<int,int> pr;
vector <int> e[2005];
int d[2005];
int fa[2005],dep[2005];
void dfs1(int x) {
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]) {
int u=e[x][i];
fa[u]=x;dep[u]=dep[x]+1;
dfs1(u);
}
}
int lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
while (dep[x]>dep[y]) x=fa[x];
while (x!=y) {
x=fa[x];
y=fa[y];
}
return x;
}
int sum[2005];
void dfs2(int x) {
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x]) {
int u=e[x][i];
dfs2(u);
sum[x]+=sum[u];
}
}
int p[2005];
bool col[2005],vis[2005];
pr num[2005],fir[2005];
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++) {
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
d[x]++;d[y]++;
}
dfs1(1);
for(int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
fir[i]=num[i]=pr(x,y);
sum[x]++;sum[y]++;
sum[lca(x,y)]-=2;
}
dfs2(1);
int ans=0;
for(int i=2;i<=n;i++) ans+=min(sum[i],2);
printf("%d\n",ans);
for(int i=2;i<=n;i++) {
int u=0;
for(int j=2;j<=n;j++)
if (d[j]==1&&!vis[j]) {
u=j;
break;
}
vis[u]=1;
int v=0;
for(int j=0;j<e[u].size();j++)
if (!vis[e[u][j]]) {
v=e[u][j];
break;
}
d[v]--;
int id1=0,id2=0;
for(int j=1;j<=m;j++)
if ((num[j].FR==u)^(num[j].SE==u)) {
if (!id1) id1=j;
else if (!id2) id2=j;
}
if (!id1&&!id2) continue;
if (!id2) {
if (num[id1].FR==u) num[id1].FR=v;
else num[id1].SE=v;
continue;
}
if (num[id1].FR==u) {
if (num[id2].FR==u) {
num[id1].FR=num[id2].SE;
col[id2]^=1;
}
else num[id1].FR=num[id2].FR;
}
else {
if (num[id2].SE==u) {
num[id1].SE=num[id2].FR;
col[id2]^=1;
}
else num[id1].SE=num[id2].SE;
}
num[id2].FR=num[id2].SE;
p[id2]=id1;
for(int j=1;j<=m;j++)
if ((num[j].FR==u)^(num[j].SE==u)) {
if (num[j].FR==u) num[j].FR=v;
else num[j].SE=v;
}
}
for(int i=1;i<=m;i++)
if (p[i]) col[i]^=col[p[i]];
for(int i=1;i<=m;i++) {
if (col[i]) swap(fir[i].FR,fir[i].SE);
printf("%d %d\n",fir[i].FR,fir[i].SE);
}
return 0;
}