【AGC014E】Blue and Red Tree

Description

  
   給定一棵\(n\)個節點的藍邊樹,再給定一棵\(n\)個節點的紅邊樹。請經過若干次操做將藍樹變成紅樹。操做要求和過程以下:
  
   1.選定一條邊全爲藍色的路徑;
  
   2.將路徑上的一條藍邊斷開,並將路徑的兩個端點之間連一條紅邊。
  
   問可否實現目標。
  
  
  spa

Solution

  
   咱們發現這個過程只會作剛好\(n-1\)次,由於每次都會減小一條藍邊、增長一條紅邊。
  
   考慮紅樹上的一條邊\((u,v)\),顯然在藍樹上操做時,咱們選擇了一條以\(u\)\(v\)爲端點的路徑,才造就了這條邊。所以紅樹上的每條邊和藍樹上的每次操做一一對應。
  
   咱們至關於要以某種順序執行這些操做:對於操做\((u,v)\),保證操做前\(u\)\(v\)連通,而後咱們斷開\(u\)\(v\)路徑上的一條邊。問全部操做可否執行完。
  
   若是咱們要執行一個操做A,那麼對於當下該路徑上的全部邊,咱們只能斷開只有A佔用的邊。形式化地講,若是對於每個未執行操做,都將路徑上的邊+1;那麼當前咱們只能斷開邊權爲1的邊。
  
   咱們想用樹剖的方式維護並模擬這個操做,但這很是難實現。由於尋找邊權爲1的邊這個操做,或許還須要樹套樹來維護,很是麻煩。
  
   正難則反,考慮整個過程反向進行:
  
   對於最後一次操做,樹上必定只剩下一條邊\((u,v)\),且最後一次操做也是\((u,v)\)。考慮倒數第二次操做,它要麼是\((u,z)\)\(z\)是從最後一次操做的\((u,v)\)這條邊延伸出的另外一條邊\((v,z)\),要麼是另成的一條獨立的邊\((x,y)\).....
  
   手玩一會,咱們發現:若是把兩棵樹建在一塊兒,那麼每次可操做的邊,就是兩棵樹中都存在的邊。操做完以後,咱們把操做邊的兩個端點縮點,繼續重複上述操做。若是操做可以執行剛好\(n-1\)次,則有解,不然無解。
  
   接下來關鍵是怎麼實現。咱們根據想的方法,將兩棵樹實實在在地建在一塊兒。用\(n\)個set維護每一個點的出邊到達點,用一個map維護兩兩點之間邊的數量。若是某兩個點之間的連邊數量等於2,則確定這條邊在兩棵樹中間都出席那了,咱們將這兩個點組成的邊塞進隊列裏,表示這個隊列裏的邊在當前均可以操做。接下來,咱們不斷從隊頭拿出邊,進行縮點操做:啓發式合併set,刪邊或加邊直接操做set和map就好。
  
   時間複雜度\(\mathcal O(n \log^2 n)\)
  
  
  code

Code

#include <cstdio>
#include <set>
#include <queue>
#include <map>
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef set<int> si;
typedef set<int>::iterator sit;
const int N=100005;
int n;
int bl[N];
si s[N];
queue<pii> q;
map<ll,int> g;
inline void swap(int &x,int &y){
    x^=y^=x^=y;
}   
int find(int u){
    return bl[u]==u?u:(bl[u]=find(bl[u]));
}
inline ll getID(int x,int y){
    if(x>y) swap(x,y);
    return 1ll*n*y+x;
}
void addEdge(int u,int v){
    s[u].insert(v);
    s[v].insert(u);
    ll eid=getID(u,v);
    int t=g[eid]+1;
    g[eid]++;
    if(t==2)
        q.push(mp(u,v));
}
void removeEdge(int u,int v){
    s[u].erase(v);
    s[v].erase(u);
    g.erase(getID(u,v));
}
void readData(){
    scanf("%d",&n);
    int u,v;
    for(int i=1;i<=(n-1)<<1;i++){
        scanf("%d%d",&u,&v);
        addEdge(u,v);
    }
}
bool solve(){
    for(int i=1;i<=n;i++) bl[i]=i;
    for(int i=1;i<n;i++){
        int u,v;
        if(q.empty())
            return false;
        u=q.front().first;
        v=q.front().second;
        q.pop();
        u=find(u); 
        v=find(v);
        if(s[u].size()>s[v].size())
            swap(u,v);
        bl[u]=v;
        removeEdge(u,v);
        for(sit i=s[u].begin(),j;i!=s[u].end();i=j){
            j=i;
            j++;
            int x=*i;
            x=find(x);
            removeEdge(u,x);
            addEdge(v,x);
        }
    }
    return true;
}
int main(){
    readData();
    puts(solve()?"YES":"NO");
    return 0;
}
相關文章
相關標籤/搜索