1. 程式人生 > >AtCoder Grand Contest 014 E:Blue and Red Tree

AtCoder Grand Contest 014 E:Blue and Red Tree

make 開始 n-1 不可 task blog 應該 space find

題目傳送門:https://agc014.contest.atcoder.jp/tasks/agc014_e

題目翻譯

有一棵有\(N\)個點的樹,初始時每條邊都是藍色的,每次你可以選擇一條由藍色邊構成的簡單路徑,讓這條路徑的兩個端點間連上一條紅邊,然後斷開這條路徑上的某條藍邊。這樣做\(N-1\)次,就可以把原本的藍樹變成紅樹。現在給你藍樹和紅樹的樣子,問你可不可能把給出的藍樹變成給出的紅樹。\(N\leqslant 10^5\)

題解

先膜一發大佬的題解:https://blog.csdn.net/sadnohappy/article/details/79478193

也許是昨天狀態不好……想了一下午都想不出……就去看題解了……

思路想到一半卡住了,看了題解的最後一步思路之後不知道怎麽實現,我覺得我還是太菜了。

先講講我一個\(O(N^2logn)\)的暴力吧:

因為每次連紅邊都要保證兩點之間有一條完全由藍邊構成的簡單路徑,然後斷開某條藍邊。所以我們把所有路徑都丟到樹上去差分一下,然後求出每條邊被多少條路徑覆蓋,每次把只被覆蓋一次的藍邊斷開,然後把那一條唯一的覆蓋它的紅邊的差分影響消去,更新一下差分值。這樣子如果做不了\(N-1\)次就是\(NO\),否則\(YES\)

順著這個思路往下想,既然我可以知道順著怎麽一條一條邊斷開,我當然也可以知道逆著怎麽一條一條邊接上來。然後我自己就想不出接下來該怎麽辦了。

因為最後一條斷開的邊肯定是紅藍都一樣的,那麽我假設這個藍邊一開始就不存在,然後把這條藍邊鏈接的兩個點合成一個點,把所有邊的信息都更新一下,我們會發現接下來刪的藍邊應該也會存在一條紅邊對應。這樣做\(N-1\)

次就行了。因為我是倒著做的,所以之前被我印點的斷開的邊在現在被我選中的路徑上還是連上的。

至於實現,看代碼吧……我基本不用\(stl\)的……但是我真的不知道還有什麽別的實現方法……

時間復雜度:\(O(NlogN)\)

空間復雜度:\(O(N)xinxi\)

代碼如下:

#include <set>
#include <map>
#include <queue>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef pair<int,int> pii;

const int maxn=1e5+5;

int n;
int fa[maxn];
queue<pii> q;
set<int>s[maxn];
map<pii,int>cnt;
set<int>::iterator it;

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

int find(int x) {
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}

int main() {
    n=read();
    for(int i=1;i<2*n-1;i++) {
        int x=read(),y=read();
        s[x].insert(y),s[y].insert(x);
        if(x>y)swap(x,y);
        pii tmp=make_pair(x,y);
        cnt[tmp]++;
        if(cnt[tmp]==2)q.push(tmp);
    }
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<n;i++) {
        if(q.empty()) {puts("NO");return 0;}
        int x=0,y=0;
        for(;x==y;x=find(q.front().first),y=find(q.front().second),q.pop());
        if(s[x].size()>s[y].size())swap(x,y);fa[x]=y;//啟發式合並,保證每條邊最多被合並logn次
        for(it=s[x].begin();it!=s[x].end();it++) {
            int v=*s[x].find(*it);
            s[v].erase(s[v].find(x));
            if(v==y)continue;
            s[v].insert(y),s[y].insert(v);
            pii tmp;
            if(v>y)tmp=make_pair(y,v);
            else tmp=make_pair(v,y);
            cnt[tmp]++;
            if(cnt[tmp]==2)q.push(tmp);
        }//更新邊的信息
    }
    puts("YES");
    return 0;
}

AtCoder Grand Contest 014 E:Blue and Red Tree