1. 程式人生 > >codeforces 962E Byteland, Berland and Disputed Cities 最小生成樹變形

codeforces 962E Byteland, Berland and Disputed Cities 最小生成樹變形

題目

題目連結

題意

Ox軸上有一堆點,這些點有三種類型RBP型,現在要求新增一些線段把這些點連起來,使得如果去掉R型別點,剩下的點都是聯通的。如果去掉B型別的點,剩下的點也是聯通的,求最小的邊長度總和。

題解

如果我們單單隻看去掉R(B)型別的點的時候,這道題毫無疑問是一道最小生成樹的題目,實際上也並不需要用一些最小生成樹的做法來做,只需要按順序掃一遍就好了。

但這個題不是,它要求這個圖去掉R(B)型別的點之後,都仍舊是最小生成樹。

我們稱這張圖為二樹圖(我胡起的,只是方便後面敘述,實際上並沒有這個名字)。

那麼如何構造這個二樹圖

呢,我們採用歸納法。

我們從左向右掃描所得到的點,並且假設在所掃描過的點中已經過構造好了部分二樹圖

那麼對於我們當前正在掃描的點,該怎麼進行處理呢?

  1. 當前的點是B(R)點,那麼我們需要把這個B(R)點與前面最近的B(R)點或者最近的P點相連線,這樣的話保證了去掉R(B)點之後,當前的這個B(R)點連線到了生成樹中,這樣保持了二樹圖的性質。

  2. 當前的點是P點,這種情況有點複雜,因為P點是不會被去掉的,所以P點影響了兩種生成樹。我們這樣考慮,為了保證兩個生成樹都要連線到當前的這個P點,對於去掉B(R)點得到的生成樹,想要連線P,那麼我們一定要讓這個

    P連線到最近的R(B)或者P點上,如果最近的是P點,那麼直接連線就好了,因為P連線到了前面的P點上,那麼這個P將同時連線到兩顆生成樹上去。
    而如果P到兩顆樹最近的點都不是P點(即上一個P與當前P點之間即有B點也有R點這種情況),我們首先讓當前的P點連線最近的B點和最近的R點,在讓當前的P點與前一個P點相連,這樣的話,對於兩顆生成樹來說都產生了一個圈,為了保持生成樹的性質,我們需要破圈。如果我們此時破了PP邊,那麼獲得優勢是cost(Pnow,Ppre),保證是二樹圖,結束。如果我們不破PP邊,那麼我們勢必要在兩個圈中各找一個最大的
    BxBx+1
    RyRy+1破掉,獲得優勢cost(Bx,Bx+1)+cost(Ry,Ry+1),保證是二樹圖,結束。因此我們比較那個優勢大就選擇那種破法。
    這裡寫圖片描述

本題至此就解決了。

程式碼

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 2e5+7;
typedef long long ll;
ll B,R,mxB,mxR,P,ans;
int n;
int main(){
    cin>>n;
    for(int i = 1;i <= n;++i){
        ll x;char c;
        scanf("%lld %c",&x,&c);
        x += 1e9+7;
        if(c == 'B'){
            if(B) ans += x - B,mxB = max(mxB,x - B);
            B = x;   
        }

        if(c == 'R'){
            if(R) ans += x - R,mxR = max(mxR,x - R);
            R = x;
        }

        if(c == 'P'){
            if(B) mxB = max(mxB,x - B),ans += x-B;
            if(R) mxR = max(mxR,x - R),ans += x-R;
            if(P) {
                ans += min(0ll,x - P - mxB - mxR);
            }

            B = R = P = x;
            mxB = mxR = 0;
        }
    }

    cout<<ans<<endl;
    return 0;
}