CF1391E Pairs of Pairs
好神仙的一道题,考后想了很久也没想出来。
考虑对原图随便跑一个 \(dfs\) 树出来。记 \(dep_i\) 为点 \(i\) 的深度,\(K=\lceil\frac{n}{2}\rceil\)。
- 如果有 \(\forall dep_i\geq K\),那么我们就找到了一条长度大于等于 \(K\) 的路径,直接输出即可。
- 否则,我们把所有点按照 \(dep_i\) 的取值分类(相同的 \(dep_i\) 分为一类),在每一类里取出 \(\lfloor \frac{cnt_i}{2}\rfloor\) 对点(任意配对,其中 \(cnt_i\) 表示深度为 \(i\) 的点的个数)。下面证明这样分组后,任意两组拼在一起的子图中边的条数 \(\leq 2\):设你取出的两对点分别为 \(\{a,b\}、\{c,d\}\),其中 \(dep_a=dep_b\le dep_c=dep_d\),显然根据无向图 \(dfs\) 树只有返祖边的性质,\(a,b\) 之间不可能有边且 \(c,d\) 之间不可能有边。\(c\) 最多与 \(a,b\) 其中的一个有边,\(d\) 同理也只与 \(a,b\) 中的一个有边。证毕。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1000009;
int head[N],cnt,n,m,dep[N],K,id[N],flag,F[N],vis[N],ans1[N],ans2[N];
struct Edge
{
int nxt,to;
}g[N*2];
void add(int from,int to)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void init()
{
scanf("%d %d",&n,&m);K=n+1>>1;
cnt=0;
for (int i=1;i<=n;i++)
F[i]=dep[i]=head[i]=vis[i]=0;
for (int x,y,i=1;i<=m;i++)
scanf("%d %d",&x,&y),
add(x,y),add(y,x);
}
void dfs(int x,int fa)
{
if(flag) return;
vis[x]=1;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||vis[v]) continue;
dep[v]=dep[x]+1,F[v]=x;
if(!flag&&dep[v]>=K-1)
{
puts("PATH");
printf("%d\n",K);
while(v)
printf("%d ",v),v=F[v];
puts("");
flag=1;
}
dfs(v,x);
}
}
bool cmp(int a,int b)
{
return dep[a]<dep[b];
}
void work()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
flag=0;
dfs(1,-1);
if(!flag)
{
puts("PAIRING");
int tot=0;
for (int i=1;i<=n;i++)
id[i]=i;
sort(id+1,id+1+n,cmp);
for (int i=1;i<=n;i++)
if(dep[id[i]]==dep[id[i+1]])
ans1[++tot]=id[i],ans2[tot]=id[i+1],i++;
printf("%d\n",tot);
for (int i=1;i<=tot;i++)
printf("%d %d\n",ans1[i],ans2[i]);
}
}
}
int main()
{
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!