1. 程式人生 > 實用技巧 >CF1391E Pairs of Pairs

CF1391E Pairs of Pairs

好神仙的一道題,考後想了很久也沒想出來。

考慮對原圖隨便跑一個 \(dfs\) 樹出來。記 \(dep_i\) 為點 \(i\) 的深度,\(K=\lceil\frac{n}{2}\rceil\)

  1. 如果有 \(\forall dep_i\geq K\),那麼我們就找到了一條長度大於等於 \(K\) 的路徑,直接輸出即可。
  2. 否則,我們把所有點按照 \(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;
}