1. 程式人生 > 其它 >Codeforces 1267 F.Foolpr¨uf Security —— 構造,拓撲

Codeforces 1267 F.Foolpr¨uf Security —— 構造,拓撲

技術標籤:2600想法拓撲排序

This way

題意:

給你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; }