zoukankan      html  css  js  c++  java
  • AGC029F Construction of a tree

    (color{#FF003F}{ exttt {AGC029F}})

    神题。
    观察下什么时候会无解,对于任意一个元素是给定集合的集合,若左侧与其中任意一个集合相连的点的数量 (geq) 集合大小,则无解。即若存在 ({E_i | 1 leq i leq n}) 的一个子集 (S),若 (f(S)=|{u in E in S}|<=|S|),则无解。
    这显然是必要条件,否则会连出环。

    构造二分图,把 (n) 个点放在左边,(n-1) 个集合放右边,点向包含的所有集合连边。如果此二分图不存在完美匹配,答案肯定是 (-1)
    反证:如果可以构造出,那么可以让每条树上的边与深度较深的点匹配,匹配数是 (n-1)

    否则左侧有且仅有一个未匹配点,从它开始dfs,每次找与其相连的未经过的右侧点,与它的对应匹配点连边,递归下去dfs。
    考虑此方法的正确性,我们需要证明这样构造出的是一棵树 且 有解不会构造不出。

    这样构造出的是一棵树:显然会有 (n-1) 条边,因为一个右侧点只会与一个左侧点匹配,所以构造不出环。

    有解不会构造不出:假设这种情况存在,那么存在一个时刻,存在右侧剩余点和左侧剩余点,且左侧经过的点与右侧剩余点无边。
    因为我们同时 dfs 到一个右侧点和与其匹配的左侧点,所以左侧经过点的数量等于右侧经过点的数量+1(一开始的未匹配点),右侧剩余点数量等于左侧剩余点数量。令 (S) 是右侧剩余点的集合,因为 (S) 中的点都有匹配点且左侧经过的点与右侧剩余点无边,所以右侧剩余点和左侧剩余点相互匹配,则 (f(S)=|S|),所以无解。

    // Author -- Frame
    
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<iostream>
    
    #define lowbit(x) ((x)&(-x))
    #define Finline __inline__ __attribute__ ((always_inline))
    #define DEBUG fprintf(stderr,"Running on Line %d in Function %s
    ",__LINE__,__FUNCTION__)
    
    typedef long long ll;
    typedef unsigned int uint;
    typedef unsigned long long ull;
    
    const int inf=0x3f3f3f3f,Inf=0x7fffffff;
    const ll INF=0x7fffffffffffffff;
    const double eps=1e-10;
    
    template <typename _Tp>_Tp gcd(const _Tp &a,const _Tp &b){return (!b)?a:gcd(b,a%b);}
    template <typename _Tp>Finline _Tp abs(const _Tp &a){return a>=0?a:-a;}
    template <typename _Tp>Finline _Tp max(const _Tp &a,const _Tp &b){return a<b?b:a;}
    template <typename _Tp>Finline _Tp min(const _Tp &a,const _Tp &b){return a<b?a:b;}
    template <typename _Tp>Finline void chmax(_Tp &a,const _Tp &b){(a<b)&&(a=b);}
    template <typename _Tp>Finline void chmin(_Tp &a,const _Tp &b){(b<a)&&(a=b);}
    template <typename _Tp>Finline bool _cmp(const _Tp &a,const _Tp &b){return abs(a-b)<=eps;}
    template <typename _Tp>Finline void read(_Tp &x)
    {
    	register char ch(getchar());
    	bool f(false);
    	while(ch<48||ch>57) f|=ch==45,ch=getchar();
    	x=ch&15,ch=getchar();
    	while(ch>=48&&ch<=57) x=(((x<<2)+x)<<1)+(ch&15),ch=getchar();
    	if(f) x=-x;
    }
    template <typename _Tp,typename... Args>Finline void read(_Tp &t,Args &...args)
    {
    	read(t);read(args...);
    }
    Finline int read_str(char *s)
    {
    	register char ch(getchar());
    	while(ch==' '||ch=='
    '||ch=='
    ') ch=getchar();
    	register char *tar=s;
    	*tar=ch,ch=getchar();
    	while(ch!=' '&&ch!='
    '&&ch!='
    '&&ch!=EOF) *(++tar)=ch,ch=getchar();
    	return tar-s+1;
    }
    
    const int N=200015;
    template<typename _Tp>
    class flow{
    public:
    	struct edge{
    		int v,nxt;
    		_Tp w;
    	}c[N<<3];
    	int front[N],edge_cnt;
    	Finline flow(){memset(front,255,sizeof(front)),edge_cnt=-1;}
    	int cur[N],dep[N],S,T;
    	int _q[N],_l,_r;
    	int node_cnt;
    	Finline void add(int u,int v,_Tp w)
    	{
    		c[++edge_cnt]=(edge){v,front[u],w},front[u]=edge_cnt;
    		c[++edge_cnt]=(edge){u,front[v],0},front[v]=edge_cnt;
    	}
    	bool bfs()
    	{
    		memset(dep,255,(node_cnt+3)<<2);
    		memcpy(cur,front,(node_cnt+3)<<2);
    		dep[S]=0;
    		_q[_l=_r=1]=S;
    		while(_l!=_r+1)
    		{
    			int x=_q[_l++];
    			for(int i=front[x];~i;i=c[i].nxt)
    			{
    				if(c[i].w&&!~dep[c[i].v])
    				{
    					dep[c[i].v]=dep[x]+1;
    					_q[++_r]=c[i].v;
    				}
    			}
    		}
    		return ~dep[T];
    	}
    	_Tp dfs(int x,int flow)
    	{
    		if(x==T||!flow) return flow;
    		_Tp f=0,rf;
    		for(int &i=cur[x];~i;i=c[i].nxt)
    		{
    			if(dep[c[i].v]==dep[x]+1&&(rf=dfs(c[i].v,min(flow,c[i].w))))
    			{
    				flow-=rf,f+=rf;
    				c[i].w-=rf,c[i^1].w+=rf;
    				if(!flow) return f;
    			}
    		}
    		return f;
    	}
    	_Tp dinic()
    	{
    		_Tp ans=0;
    		while(bfs()) ans+=dfs(S,inf);
    		// inf changes when _Tp != int
    		return ans;
    	}
    	bool vis[N];
    	int to[N];
    	int n;
    	std::pair<int,int> ans[N];
    	int cnt=0;
    	void dfs(int x)
    	{
    		for(int i=front[x];~i;i=c[i].nxt)
    		{
    			if(c[i].v<n+n&&!vis[c[i].v-n])
    			{
    				++cnt;
    				ans[c[i].v-n]=std::make_pair(x,to[c[i].v-n]);
    				vis[c[i].v-n]=true;
    				dfs(to[c[i].v-n]);
    			}
    		}
    	}
    	void solve(int _n)
    	{
    		n=_n;
    		int root=0;
    		for(int i=front[S];~i;i=c[i].nxt)
    		{
    			if(c[i].w)
    			{
    				root=c[i].v;
    				break;
    			}
    		}
    		for(int i=1;i<n;++i)
    		{
    			for(int _=front[i+n];~_;_=c[_].nxt)
    			{
    				if(c[_].v<=n&&c[_].w)
    				{
    					to[i]=c[_].v;
    					break;
    				}
    			}
    		}
    		dfs(root);
    		if(cnt!=n-1)
    		{
    			printf("-1
    ");
    		}
    		else
    		{
    			for(int i=1;i<n;++i) printf("%d %d
    ",ans[i].first,ans[i].second);
    		}
    	}
    };
    flow<int> F;
    int main()
    {
    	int n;
    	read(n);
    	int x,y;
    	F.S=n+n,F.T=n+n+1;
    	F.node_cnt=n+n+1;
    	for(int i=1;i<n;++i)
    	{
    		read(x);
    		for(int j=1;j<=x;++j)
    		{
    			read(y);
    			F.add(y,i+n,1);
    		}
    		F.add(i+n,F.T,1);
    		F.add(F.S,i,1);
    	}
    	F.add(F.S,n,1);
    	int ans=F.dinic();
    	if(ans!=n-1)
    	{
    		printf("-1
    ");
    		return 0;
    	}
    	F.solve(n);
    	return 0;
    }
    
  • 相关阅读:
    PB中的函数ProfileString
    PB做大屏显示滚动窗口 [转载]
    PB对象(sqlca、sqlsa、sqlda)[转载]
    SQL中 EXCEPT、INTERSECT用法[转载]
    第一篇博客
    Intelli IDEA 炫酷插件
    概念总结
    秒杀系统(四)——异常处理和常量的处理
    秒杀系统(三)——实现哪些秒杀系统功能
    秒杀系统(二)
  • 原文地址:https://www.cnblogs.com/Frame233/p/12632262.html
Copyright © 2011-2022 走看看