1. 程式人生 > >[洛谷P3621] [APIO2007] 風鈴

[洛谷P3621] [APIO2007] 風鈴

sample algo 樹形dp source esp 也會 個數 urn mes

Description

你準備給弟弟 Ike 買一件禮物,但是,Ike 挑選禮物的方式很特別:他只喜歡那些能被他排成有序形狀的東西。
你準備給 Ike 買一個風鈴。風鈴是一種多層的裝飾品,一般掛在天花板上。
每個風鈴都包含一些由豎直線連起來的水平桿。每根桿的兩頭都有線連接,下面或者掛著另一根水平桿,或者掛著一個玩具。下面是一個風鈴的例子:

技術分享圖片

為了滿足弟弟,你需要選一個滿足下面兩個條件的風鈴:

(1) 所有的玩具都在同一層(也就是說,每個玩具到天花板之間的桿的個數是一樣的)或至多相差一層。

(2) 對於兩個相差一層的玩具,左邊的玩具比右邊的玩具要更靠下一點。

風鈴可以按照下面的規則重新排列:任選一根桿,將桿兩頭的線“交換”。也就是解開一根桿左右兩頭的線,然後將它們綁到桿的另一頭。這個操作不會改變更下面的桿上線的排列順序。正在訓練信息學奧林匹克的你,決定設計一個算法,判斷能否通過重新排列,將一個給定的風鈴變為 Ike 喜歡的樣子。
考慮上面的例子,上圖中的風鈴滿足條件(1),卻不滿足條件(2)——最左邊的那個玩具比它右邊的要高。
但是,我們可以通過下面的步驟把這個風鈴變成一個 Ike 喜歡的:
第一步,將桿 1 的左右兩邊交換,這使得桿 2 和桿 3 的位置互換,交換的結果如下圖所示:

技術分享圖片

第二步,也是最後一步,將桿 2 的左右兩邊交換,這使得桿 4 到了左邊,原來在左邊的玩具到了右邊,交換的結果發下圖所示:

技術分享圖片

現在的這個風鈴就滿足 Ike 的條件了。

你的任務是:給定一個風鈴的描述,求出最少需要多少次交換才能使這風鈴滿足 Ike 的條件(如果可能)

Input

輸入的第一行包含一個整數 n(1≤n≤100 000),表示風鈴中有多少根桿。

接下來的 n 行描述桿的連接信息。這部分的第 i 行包含兩個由空格分隔的整數 li和 ri,描述桿 i 的左右兩邊懸掛的東西。如果掛的是一個玩具,則對應的值為-1,否則為掛在下面的桿的編號

Output

輸出僅包含一個整數。表示最少需要多少次交換能使風鈴滿足 Ike 的條件。如果不可能滿足,輸出-1。

Sample Input

6
2 3
-1 4
5 6
-1 -1
-1 -1
-1 -1

Sample Output

2


想法

才不會說我是看這個題目好玩才去做的呢
這個題目讓我想起了林清玄的散文《風鈴》:

有了風鈴,風雖然吹過了,還留下美妙的聲音
有了心的風鈴,生命即使走過了,也會留下動人的痕跡
每一次起風的時候,每一步歲月的腳步,都會那樣真實地存在。

等等,跑題了!

這個題就是樹形dp嘛,註意判斷幾種不行的情況:
1.風鈴相差層數>1
2.在滿足風鈴相差層數為1的情況下,把在上面一層的風鈴統稱為F,把下面一層的風鈴統稱為G
在某一節點,其兩個子節點中都既有F又有G
然後註意各種細節就好了(自古樹形dp細節多qwq)


代碼

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100005;

int ch[N][2],pa[N],dep[N];
int n,rt;

int m,d[N],t;
void get_dep(int u){
    for(int i=0;i<2;i++){
        if(ch[u][i]!=-1){
            dep[ch[u][i]]=dep[u]+1;
            get_dep(ch[u][i]);
        }
        else d[m++]=dep[u]+1,t=max(t,d[m-1]);
    }
}
int sz[N],num[N]; //num表示該節點子樹中F與G的總和,sz表示該節點子樹中F個數
void dfs0(int u){
    for(int i=0;i<2;i++){
        if(ch[u][i]!=-1){
            dfs0(ch[u][i]);
            num[u]+=num[ch[u][i]]; sz[u]+=sz[ch[u][i]];
        }
        else {
            num[u]++;
            if(dep[u]+1==t-1) sz[u]++;
        }
    }
}

int flag;
int dfs(int u){
    if(sz[u]==0 || sz[u]==num[u]) return 0;
    if(ch[u][0]!=-1 && ch[u][1]!=-1){
        if(sz[ch[u][0]]==0) return dfs(ch[u][1]);
        if(sz[ch[u][1]]==0) return dfs(ch[u][0])+1;
        if(sz[ch[u][0]]==num[ch[u][0]]) return dfs(ch[u][1])+1;
        if(sz[ch[u][1]]==num[ch[u][1]]) return dfs(ch[u][0]);
        flag=0; return 0;
    }
    if(ch[u][0]==-1 && ch[u][1]!=-1){
        if(sz[ch[u][1]]==0) return 1;
        return dfs(ch[u][1])+1;
    }
    if(ch[u][0]!=-1 && ch[u][1]==-1){
        if(sz[ch[u][0]]==0) return 0;
        return dfs(ch[u][0]);
    }
    return 0;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&ch[i][0],&ch[i][1]);
        if(ch[i][0]!=-1) pa[ch[i][0]]=i;
        if(ch[i][1]!=-1) pa[ch[i][1]]=i;
    }
    
    for(int i=1;i<=n;i++) if(!pa[i]) { rt=i; break; }
    dep[rt]=1; get_dep(rt);
    
    flag=1;
    for(int i=0;i<m;i++) if(d[i]<t-1) flag=0;
    if(flag==0) { printf("-1"); return 0; }
    
    dfs0(rt);
    if(sz[rt]==0) { printf("0"); return 0; }
    
    int ans=dfs(rt);
    if(flag==0) printf("-1");
    else printf("%d",ans);
    
    return 0;
}

[洛谷P3621] [APIO2007] 風鈴