AT4505-[AGC029F]Construction of a tree【构造题,hall定理,网络流】
正題
題目鏈接:https://www.luogu.com.cn/problem/AT4505
題目大意
給出nnn個點和n?1n-1n?1個點集UiU_iUi?,每個點集中選擇兩個點連邊使得該圖是一棵樹。求方案。
n∈[1,105],∑i=1n?1∣Ui∣∈[1,2?105]n\in[1,10^5],\sum_{i=1}^{n-1} |U_i|\in[1,2*10^5]n∈[1,105],∑i=1n?1?∣Ui?∣∈[1,2?105]
解題思路
冬令營上講的題目,現在來寫。(而且好像我記得課上講的做法是bitsetbitsetbitset的,還是時間久了我記岔了?)
第一眼看上去直覺像是hallhallhall定理但還是不會。
hall定理:2?n2*n2?n個點的二分圖匹配,如果滿足任意kkk個點都連接了不少于kkk個點的話,那么這張圖就有完全匹配。
先套一下試試,發現滿足條件的圖對于它的每個子圖SSS滿足該子圖是一個森林。
換句話說對于任意一個UUU的集合TTT,G(T)G(T)G(T)表示選出的邊連接的節點個數,那么一定有G(T)≥∣T∣+1G(T)\geq |T|+1G(T)≥∣T∣+1
回顧一下hallhallhall定理發現是不是很像。
可以先給每個點集選出一個各不同的點(也就是跑一次匹配),如果選不出來那么顯然無解。
然后考慮另一個點的選擇,從沒有被選擇的那個點入手,這個點可以選擇任何一個包含它的點集連接出去,然后就從下一個點集開始,直到回溯回來選擇下一個。如果最后能夠遍歷所有點就是合法的。
考慮一下正確性,如果它不能遍歷所有點那么沒有被遍歷的點集TTT無論怎么連接外面,就一定有一個環不滿足G(T)≥∣T∣+1G(T)\geq |T|+1G(T)≥∣T∣+1。如果它能遍歷所有點,那么我們已經構造出一個方案,顯然合法。
時間復雜度O(∑i=1n?1∣E∣n+n)O(\sum_{i=1}^{n-1}|E|\sqrt n+n)O(∑i=1n?1?∣E∣n?+n)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=4e5+10,inf=1e9; struct node{int to,next,w; }a[N*2]; int n,s,t,tot=1,cnt,ans; int ls[N],p[N],b[N],dep[N],cur[N]; queue<int> q;bool v[N]; void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;return; } bool bfs(){for(int i=1;i<=t;i++)cur[i]=ls[i],dep[i]=0;while(!q.empty())q.pop();q.push(s);dep[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[y]||!a[i].w)continue;dep[y]=dep[x]+1;if(y==t)return 1;q.push(y);}}return 0; } int dinic(int x,int flow){if(x==t)return flow;int rest=0,k;for(int &i=cur[x];i;i=a[i].next){int y=a[i].to;if(dep[x]+1!=dep[y]||!a[i].w)continue;rest+=(k=dinic(y,min(a[i].w,flow-rest)));a[i].w-=k;a[i^1].w+=k;if(rest==flow)return rest;}if(!rest)dep[x]=0;return rest; } void dfs(int x){cnt++;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(v[y])continue;b[y]=x;v[y]=1;dfs(p[y]);} } int main() {scanf("%d",&n);s=2*n;t=s+1;for(int i=1;i<n;i++){int m;scanf("%d",&m);for(int j=1;j<=m;j++){int x;scanf("%d",&x);addl(x,i+n,1);}}for(int i=1;i<=n;i++)addl(s,i,1);for(int i=1;i<n;i++)addl(i+n,t,1);while(bfs())ans+=dinic(s,inf);if(ans<n-1)return puts("-1")&0;for(int x=n+1;x<s;x++)for(int i=ls[x];i;i=a[i].next)if(a[i].w){p[x]=a[i].to;break;}v[s]=1;for(int i=ls[s];i;i=a[i].next)if(a[i].w)dfs(a[i].to);if(cnt<n)return puts("-1")&0;for(int x=n+1;x<s;x++)printf("%d %d\n",p[x],b[x]);return 0; }總結
以上是生活随笔為你收集整理的AT4505-[AGC029F]Construction of a tree【构造题,hall定理,网络流】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在电脑上使用手机剪映中的模板在电脑上使用
- 下一篇: 路由器安装在弱电箱里路由器应该怎么接在弱