Codeforces 1267 F.Foolpr¨uf Security —— 構造,拓撲
阿新 • • 發佈:2020-12-25
題意:
給你n+m個點,組成一棵樹,並且左半邊n個點,右半邊m個點組成一張二分圖。
定義 一棵樹的Pr¨ufer code:
取出當前下標最小的葉子結點,將其刪除,並且輸出和它相鄰的點的下標,直到最後只剩下兩個點。
告訴你兩個子序列a,b。他們都是這個 Pr¨ufer code的一個子序列,並且a中元素是1~n,b中元素為n+1~n+m
問你是否能構造這樣一棵樹,並且將這棵樹輸出
題解:
首先可以明確的一點,就是如果a的大小>m-1或者b的大小>n-1.就是不行的,因為最後要剩下兩個點。
然後感覺一下是一定可以構造出來的,因為你只需要按它所說的步驟去連邊,這樣子除了第一個數其它數都會至少有一個入邊,一個出邊,所以一定會在前一個數輸出之後才會輸出。那麼對於最後一個數,只需要將兩邊的最後一個數連邊就可以保證合法性。 那麼做法就是用拓撲的方法,先將所有度數為0的點放到優先佇列中,取出第一個元素之後檢視是屬於哪個部分,接下來只需要將它和另一個區域列舉到的數連邊,然後再將那個數的度數-1。最後如果給你的a或者b用完了,只需要將剩下的點連到另一個部分的最後一個點就行了。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int deg[N],a[2][N];
priority_queue<int,vector<int>,greater<int> >Q;
int main()
{
int n,m,k[2];
scanf("%d%d%d%d",&n,&m,&k[0],&k[1]);
if(k[0]>m-1||k[1]>n-1)return 0*printf("No\n");
printf("Yes\n");
for(int i=1;i<=k[0];i++)
scanf("%d",&a[0][i]),deg[a[0][i]]++;
for(int i=1;i<=k[1];i++)
scanf ("%d",&a[1][i]),deg[a[1][i]]++;
deg[a[0][k[0]]]=deg[a[1][k[1]]]=1e9;
for(int i=1;i<=n+m;i++)
if(!deg[i])Q.push(i);
int now[2]={1,1};
while(!Q.empty()){
int u=Q.top();Q.pop();
int p=1;
if(u>n)p=0;
printf("%d %d\n",u,a[p][now[p]]);
deg[a[p][now[p]]]--;
if(!deg[a[p][now[p]]])Q.push(a[p][now[p]]);
now[p]=min(now[p]+1,k[p]);
}
printf("%d %d\n",a[0][k[0]],a[1][k[1]]);
return 0;
}