Codeforces 611H

简单转化一下模型,变为给定 k ≤ 6 k\leq 6 k6个集合,知道每个集合间的边的数目,要求构造一棵合法的树。
注意到如果有解,一定存在一个方案,使得每个集合至多一个点不为叶子。于是有解的话一定可以从每个集合中取出一个点,构造一棵生成树,再将剩余的点连到这棵 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(kk2(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值