正题
这题好难,首先考虑转化为一棵有根树,令1为根,想办法从每个集合中选出一些边,按照一定顺序加入,使得每一个xi只被选一次,并且按照一定顺序加入,使得
已经在树里面就可以了。
首先要满足第一个条件,xi肯定不能为1,所以我们二分图匹配中右边放得点只能为[2,n]。然后向自己集合内的元素连边,若找到了一组匹配,考虑直接用队列加入就可以了,一个xi可以加入树中当且仅当集合内有元素已经在树里面,那么就直接认它为fa。
如果最后并不能把所有元素加进去,说明存在一个时刻,使得未加入树内的集合中不包含已经在树中的元素。
考虑另一种匹配可不可能构成一种有解的方案,另一种匹配只可能交换几个xi,考虑交换的xi是在前面已经在树内的集合之间的,显然不可能产生有解的方案,因为前面的已经构造出一棵树了, 所以没有影响,如果在后面没有加进树中的集合之间呢?显然也不会产生有解的方案,因为后面集合的并集中不包含前面树内的任一元素,而前后两种集合之间又不能交换xi,所以我们只需要直到任一一种匹配不可行,那么其他匹配都是不可行的。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
int y,next,c;
}s[N<<3];
struct op{
int x,y;
}we[N];
int first[N<<1],len=0,n,m,bg,nd,c[N<<1],d[N<<1],las[N];
int lis[N<<1],st,ed;
vector<int> P[N];
void ins(int x,int y){
s[len++]=(edge){y,first[x],1};first[x]=len-1;
s[len++]=(edge){x,first[y],0};first[y]=len-1;
}
bool bfs(){
lis[st=1]=bg;ed=2;
memset(d,0,sizeof(d));d[0]=1;
while(st!=ed){
int x=lis[st];st++;
for(int i=first[x];i!=-1;i=s[i].next) if(!d[s[i].y] && s[i].c)
d[s[i].y]=d[x]+1,lis[ed++]=s[i].y;
}
return d[nd];
}
int dfs(int x,int t){
if(x==nd) return t;
int tot=0,my;
for(int i=first[x];i!=-1;i=s[i].next) if(d[s[i].y]==d[x]+1 && s[i].c){
my=dfs(s[i].y,min(t-tot,s[i].c));tot+=my;
if(my && x>=1 && x<=n-1 && s[i].y) c[x]=s[i].y-n+2;
s[i].c-=my;s[i^1].c+=my;
if(t==tot) break;
}
if(!tot) d[x]=0;
return tot;
}
int Dinic(){
int dx,ans=0;
while(bfs()){
dx=dfs(bg,1e9);
while(dx) ans+=dx,dx=dfs(bg,1e9);
}
return ans;
}
int main(){
scanf("%d",&n);bg=0,nd=2*n-1;
int x;
memset(first,-1,sizeof(first));
for(int i=1;i<n;i++){
ins(bg,i);
ins(i+n-1,nd);
scanf("%d",&m);
while(m--){
scanf("%d",&x);
P[x].push_back(i);
if(x-1) ins(i,n-1+x-1);
}
}
if(Dinic()==n-1){
st=ed=1;
for(int i=0;i<P[1].size();i++) lis[ed++]=P[1][i],las[P[1][i]]=1;
while(st!=ed){
int x=lis[st];st++;
we[x]=(op){las[x],c[x]};
for(int i=0;i<P[c[x]].size();i++) if(!las[P[c[x]][i]]) lis[ed++]=P[c[x]][i],las[P[c[x]][i]]=c[x];
}
if(ed==n) for(int i=1;i<n;i++) printf("%d %d\n",we[i].x,we[i].y);
else printf("-1\n");
}
else printf("-1\n");
}
317

被折叠的 条评论
为什么被折叠?



