#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<vector>
#include<map>
#include<algorithm>
#define ls o<<1
#define rs o<<1|1
using namespace std;
typedef long long LL;
const int N=5e4+10;
bool e[N];
int f[N],com[N],head[N];
vector<int>a[N];//a[]表示原图
vector<int>b[N];//b[]表示记录答案的路径
int n,m,k,x,y;
int ans;
int find(int x)
{
if(f[x]==x)return f[x];
f[x]=find(f[x]);
return f[x];
}
void dfs(int x)
{
e[x]=1;
int st=0;
for(int i=a[x].size()-1;~i;--i)
{
int y=a[x][i];
if(e[y])continue;
dfs(y);
if(com[y])
{
if(st==0)
{
st=com[y];
b[st].push_back(x);
}
else
{
int ed=com[y];
for(int j=b[ed].size()-1;~j;--j)b[st].push_back(b[ed][j]);
head[++ans]=st;
st=0;
}
}
}
if(st==0)
{
if(com[x])b[x].push_back(x);
}
else//st exist
{
if(com[x])
{
head[++ans]=st;
com[x]=0;
}
else com[x]=st;
}
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&k))
{
for(int i=1;i<=n;i++)
{
e[i]=0;
f[i]=i;
a[i].clear();
b[i].clear();
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
int fx=find(x);
int fy=find(y);
if(fx==fy)continue;
f[fy]=fx;
a[x].push_back(y);
a[y].push_back(x);
}
while(k--)
{
scanf("%d",&x);
com[x]=x;
}
ans=0;
for(int i=1;i<=n;i++)if(e[i]==0)dfs(i);
printf("%d\n",ans);
for(int i=1;i<=ans;i++)
{
int x=head[i];
int g=b[x].size();
printf("%d ",g-1);
for(int j=0;j<g;j++)printf("%d ",b[x][j]);
puts("");
}
}
}
/*
【trick&&吐槽】
题目难下手?
构造样例多观察。
找出结论再思考。
HDU只有我一个人做了出来啦啦啦~
【题意】
有n(50000)个点,m(50000)条边,其中有k(1<=k<=n)个点是特殊点。
我们想要在特殊点之间画路径,要求:
1,每个特殊点最多是一条路径的端点
2,原图中的每条边最多只被画一次
3,使得路径数尽可能多。
【类型】
脑洞,构造
【分析】
这题乍一想确实是很难想,然而——我们还是有迹可循的。
1[观察],数据规模如此之大,必须用O(n)或者O(nlogn)之级别的算法
2[猜想],我们经过对样例的观察,以及思考,发现:对于一个包含w个点联通块,可以画出的路径数一定是[w/2]
首先画一画找不到反例,于是我们顺着这个思路往下展开——
因为树是最基本的联通块,而且不改变刚才那个猜想,且很多问题在图上很难思考。
所以我们不妨把这个图生成任意一个生成树,然后去构造路径。
3[思考],在树上怎么思考?
一个比较容易的思考方式,是沿子树展开。
首先我们发现,从一棵子树的内部,最多向外展开一条路径。
于是我们对于这棵子树,对于根节点下方每个有出路径的子节点。
A,如果(出路径数+根节点的重要度)为偶数,每个pair我们就合并再一起。没有外延展。
B,如果(出路径数+根节点的重要度)为奇数,每个pair我们就合并再一起。然后多出一个外展。
我们发现这种构造,不仅证明了猜想的正确性,而且这道题就可以AC啦。
==================================================================================
剩下的最后一个问题——如何实现[3]?
我们如果用b[]来记录路径,因为每个点可能被push_back进去多次。所以我们是不太容易设定后继的。
我们不妨把所有出路径的子节点,连向父节点。
然后遇到要合并的pair,我们就把其中第二个路径反一下再连起来。
这样最后就能AC啦!
【时间复杂度&&优化】
O(n)
*/
【2015-2016 ACM-ICPC, NEERC, Southern Subregional Contest H】【观察找规律 脑洞 构造】Tourist Guide 关键点作端点最多路径
最新推荐文章于 2016-10-08 16:48:06 发布