CSP-S模拟 电话节(构造)

传送门
貌似跟图没啥关系啊。。。
随便找出一种生成森林出来,可以通过归纳法证明任意一棵子树最多有一个特殊点没有被匹配。
然后就做完啦。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return *ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef pair<int,int> pii;
const int N=50005;
int n,m,k,dep[N],siz[N],fa[N],top[N],hson[N];
vector<int>e[N],g[N];
vector<pii>ans;
bool exi[N],vis[N];
int dfs1(int p){
	vis[p]=1,hson[p]=0,siz[p]=1;
	vector<int>ss;
	if(exi[p])ss.push_back(p);
	for(ri t,i=0,v;i<e[p].size();++i){
		if(vis[v=e[p][i]])continue;
		g[p].push_back(v);
		fa[v]=p,dep[v]=dep[p]+1,t=dfs1(v),siz[p]+=siz[v];
		if(t)ss.push_back(t);
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
	int a,b;
	while(ss.size()>1){
		a=ss.back();
		ss.pop_back();
		b=ss.back();
		ss.pop_back();
		ans.push_back(pii(a,b));
	}
	return ss.size()?ss.back():0;
}
void dfs2(int p,int tp){
	top[p]=tp;
	if(!hson[p])return;
	dfs2(hson[p],tp);
	for(ri i=0,v;i<g[p].size();++i)if((v=g[p][i])^hson[p])dfs2(v,v);
}
inline int lca(int x,int y){
	while(top[x]^top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]]; 
	}
	return dep[x]<dep[y]?x:y;
}
inline void print(int x,int y){
	int t=lca(x,y);
	cout<<dep[x]+dep[y]-2*dep[t]<<' ';
	vector<int>res;
	while(1){
		res.push_back(x);
		if(x==t)break;
		x=fa[x];
	}
	for(ri i=0,up=res.size();i<up;++i)cout<<res[i]<<' ';
	res.clear();
	while(y!=t){
		res.push_back(y);
		y=fa[y];
	}
	reverse(res.begin(),res.end());
	for(ri i=0,up=res.size();i<up;++i)cout<<res[i]<<' ';
	puts("");
}
int main(){
	n=read(),m=read(),k=read();
	for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	for(ri i=1;i<=k;++i)exi[read()]=1;
	for(ri i=1;i<=n;++i)if(!vis[i])dfs1(i),dfs2(i,i);
	cout<<ans.size()<<'\n';
	for(ri i=0;i<ans.size();++i)print(ans[i].fi,ans[i].se);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值