简单转化一下模型,变为给定
k
≤
6
k\leq 6
k≤6个集合,知道每个集合间的边的数目,要求构造一棵合法的树。
注意到如果有解,一定存在一个方案,使得每个集合至多一个点不为叶子。于是有解的话一定可以从每个集合中取出一个点,构造一棵生成树,再将剩余的点连到这棵
k
k
k个点的生成树上。
由于
k
k
k非常小,我们可以枚举这棵生成树的所有形态,只需判定是否可以将剩余的点挂到生成树上。原图中每条没有使用的边可以给边两端集合中的点使用,这样可以构造一个
O
(
k
2
)
\mathcal O(k^2)
O(k2)个点的二分图,左部图为集合,右部图为边集,变为判定是否有完备匹配。为了加快速度,可以用hall定理
O
(
2
k
)
\mathcal O(2^k)
O(2k)判定,最后输出方案用dinic即可。
时间复杂度
O
(
k
k
−
2
(
2
k
+
k
2
)
+
n
)
\mathcal O(k^{k-2}(2^k+k^2)+n)
O(kk−2(2k+k2)+n)。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
struct Edge {
int t,f,next;
Edge() {}
Edge(int a,int b,int c):t(a),f(b),next(c) {}
};
Edge e[1000];
int head[50],vs,vt,tot=-1;
void addEdge(int x,int y,int z) {
e[++tot]=Edge(y,z,head[x]);
head[x]=tot;
e[++tot]=Edge(x,0,head[y]);
head[y]=tot;
}
namespace Flow {
int d[50],cur[50];
queue <int> q;
bool bfs() {
while (!q.empty()) q.pop();
memset(d,255,sizeof(d));
d[vs]=0;cur[vs]=head[vs];
q.push(vs);
while (!q.empty()) {
int x=q.front();q.pop();
for(int i=head[x];i!=-1;i=e[i].next)
if (e[i].f&&d[e[i].t]==-1) {
int u=e[i].t;
d[u]=d[x]+1;
if (u==vt) return 1;
cur[u]=head[u];
q.push(u);
}
}
return 0;
}
int dfs(int x,int a) {
if (x==vt||!a) return a;
int ans=0;
for(int &i=cur[x];i!=-1;i=e[i].next)
if (e[i].f&&d[e[i].t]==d[x]+1) {
int u=e[i].t;
int f=dfs(u,min(a,e[i].f));
if (f) {
e[i].f-=f;
e[i^1].f+=f;
ans+=f;
a-=f;
if (!a) break;
}
}
return ans;
}
int maxflow() {
int ans=0;
while (bfs())
ans+=dfs(vs,inf);
return ans;
}
}
const int bound[7]={0,1,10,100,1000,10000,100000};
int a[10][10],size[10],L;
int p[10];
void check(int n) {
static int f[1<<6],g[1<<6],id[50][2];
for(int i=2;i<=L;i++) {
int x=i;
for(int j=1;j<L;j++) x=p[x];
if (x!=1) return;
}
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int i=1;i<=L;i++)
for(int j=i;j<=L;j++) {
f[(1<<(i-1))|(1<<(j-1))]-=a[i][j];
f[1<<(i-1)]+=a[i][j];
f[1<<(j-1)]+=a[i][j];
}
for(int i=2;i<=L;i++) {
f[(1<<(i-1))|(1<<(p[i]-1))]++;
f[1<<(i-1)]--;
f[1<<(p[i]-1)]--;
}
for(int i=1;i<=L;i++) g[1<<(i-1)]=size[i]-1;
for(int i=1;i<(1<<L);i++) {
if (lowbit(i)!=i) f[i]+=f[lowbit(i)]+f[i^lowbit(i)];
if (lowbit(i)!=i) g[i]+=g[lowbit(i)]+g[i^lowbit(i)];
if (f[i]<g[i]) return;
}
for(int i=2;i<=L;i++) {
a[i][p[i]]--;
a[p[i]][i]--;
}
int sz=0;
vs=0;vt=L+L*L+1;
for(int i=1;i<=L;i++)
for(int j=i;j<=L;j++)
if (a[i][j]) {
sz++;
id[sz][0]=i;id[sz][1]=j;
addEdge(L+sz,vt,a[i][j]);
}
for(int i=1;i<=L;i++) {
addEdge(vs,i,size[i]-1);
for(int j=1;j<=sz;j++)
if (id[j][0]==i||id[j][1]==i) addEdge(i,L+j,inf);
}
Flow::maxflow();
for(int i=2;i<=L;i++) printf("%d %d\n",bound[i],bound[p[i]]);
for(int i=1;i<=L;i++) {
int cnt=0;
for(int j=head[i];j!=-1;j=e[j].next)
if (e[j].t!=vs) {
int u=((id[e[j].t-L][0]!=i)?id[e[j].t-L][0]:id[e[j].t-L][1]);
for(int k=1;k<=e[j^1].f;k++) printf("%d %d\n",bound[i]+(++cnt),bound[u]);
}
}
exit(0);
}
void dfs(int d,int n) {
if (d>L) {
check(n);
return;
}
for(int i=1;i<=L;i++)
if (d!=i&&a[d][i]) {
p[d]=i;
dfs(d+1,n);
}
}
int main() {
memset(head,255,sizeof(head));
int n;
scanf("%d",&n);
while (L<6&&bound[L+1]<=n) L++;
for(int i=1;i<L;i++) size[i]=bound[i+1]-bound[i];
size[L]=n-bound[L]+1;
for(int i=1;i<n;i++) {
char s1[10],s2[10];
scanf("%s%s",s1,s2);
int u=strlen(s1),v=strlen(s2);
a[u][v]++;
if (u!=v) a[v][u]++;
}
p[1]=1;
dfs(2,n);
puts("-1");
return 0;
}