1. 程式人生 > 其它 >[LeetCode] 1239. Maximum Length of a Concatenated String with Unique Characters 串聯字串的最大長度

[LeetCode] 1239. Maximum Length of a Concatenated String with Unique Characters 串聯字串的最大長度

目錄

笛卡爾樹學習筆記

1.笛卡爾樹介紹

​ 笛卡爾樹是一種二叉樹,與平衡樹和堆有著密切的聯絡。其每個節點由二元組 $(k,w)$ 組成。

2.笛卡爾樹構造

​ 笛卡爾樹的第一鍵值 $k$ 應滿足 $BST$ (中序遍歷有序)的性質。第二鍵值應滿足堆(小根堆/大根堆)的性質。

一般情況下會把陣列下標當作第一鍵值,把陣列的權值當作第二鍵值。極少數情況下會有任意給定的 $(k,w)$ 。

​ 具體實現時,用單調棧維護從根節點開始的右鏈。我們先按照 $k$ 排序,然後一個一個插入。這樣的建樹複雜度為 $O(n)$ 。

for(int i=1;i<=n;i++){
    int now=top;
    while(now>0 && tr[i].w<tr[st[now]].w){
        now--;
    }
    if(now){
        tr[st[now]].ch[1]=i;
    }
    if(now<top){
        tr[i].ch[0]=st[now+1];
    }
    st[++now]=i;
    top=now;
}

3.笛卡爾樹性質(小根堆為例)

  1. 以笛卡爾樹上任意一點為根的子樹是一段連續極長區間。$w_u$ 是區間最小值,區間在保證最小值不變的情況下不能再向兩邊延伸。
  2. 區間 $[a,b]$ 的最小值為 $w_{lca(a,b)}$ 。
  3. 若 $k,w$ 都互不相同,那麼笛卡爾樹形態結構唯一。
  4. 對於一個有序的 $k$ 和無序的 $w$ 。笛卡爾樹上的任意一點期望高度為 $E(depth_i) = H(i)+H(n-i+1)-1$ 。其中 $H(n)=\sum\limits_{i=1}^n\dfrac{1}{i} $ (調和級數)。因此笛卡爾樹期望樹高為 $log\ n$ 。
  5. $Treap$ 是一種特殊的笛卡爾樹,可以運用 $Treap$ 操作維護笛卡爾樹。
  6. 笛卡爾樹主要解決最大/最小值問題,以及(通過記錄時間)關於插入刪除的問題

4.例題

  1. [Largest Rectangle in a Histogram](

    Problem - 1506 (hdu.edu.cn))

    這道題可以單調棧,但是也可以笛卡爾樹。以下標為 $k$ ,矩陣高為 $w$ ,建立笛卡爾樹(小根堆)。

    不難發現,笛卡爾樹上每個點的子樹大小,就是他的權值高度所能擁有的最大橫向距離(最長區間就是子樹大小)。故節點 $u$ 的最大子矩陣就是 $ Size_u \times w_u$ 。

    Code:

struct Cartesian{
    int k,w;
    int ch[2];
};
Cartesian tr[DMAX];
int siz[DMAX];
void DFS(int now){
    if(!now){
        return ;
    }
    siz[now]=1;
    DFS(tr[now].ch[0]);
    siz[now]+=siz[tr[now].ch[0]];
    DFS(tr[now].ch[1]);
    siz[now]+=siz[tr[now].ch[1]];
}
int main(){
    while(scanf("%d",&n)){
        if(n==0){
            break;
        }
        for(int i=1;i<=n;i++){
            read(a[i]);
            tr[i].k=i,tr[i].w=a[i];
            tr[i].ch[0]=tr[i].ch[1]=0;
        }
        top=0;
        build();
        mem(siz,0);
        DFS(st[1]);
        ll ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,1ll*siz[i]*tr[i].w);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  1. [笛卡爾樹](P5854 【模板】笛卡爾樹 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn))

    笛卡爾樹板子題。沒有什麼奇技淫巧,直接算就行了。

  2. [樹的序]([P1377 TJOI2011]樹的序 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn))

    還是一個板子題,但是這次要以權值為 $k$ 值,以下標為 $w$ 值。注意輸出時為先序遍歷。

    Code:

    struct Cartesian{
        int k,w;
        int ch[2];
        int fa;
        bool operator <(const Cartesian &p) const{
            return k<p.k;
        }
    };
    Cartesian tr[DMAX];
    void DFS(int now){
        if(!now){
            return ;
        }
        printf("%d ", tr[now].k);
        DFS(tr[now].ch[0]);
        DFS(tr[now].ch[1]);
        return ;
    }
    int main(){
        read(n);
        for(int i=1;i<=n;i++){
            read(a[i]);
            tr[i].k=a[i],tr[i].w=i;
        }
        sort(tr+1,tr+n+1);
        build();
        DFS(st[1]);
        return 0;
    }
    

    4.[Periodni](SPOJ.com - Problem PERIODNI)

    ​ 笛卡爾樹+樹形DP

    ​ 我們以下標為 $k$ 值,高度為 $w$ 值建立笛卡爾樹。這時,我們把整個圖劃分為了很多個小矩形。

    ​ 我們首先先考慮一個小情況:對於 $n*m$ 的矩形,在其中選 $k$ 個點的方案數。答案是:$\binom{n}{k}\times\binom{m}{k}\times k!$ 。考慮其組合意義加以證明,在 $n$ 行中選出 $k$ 行,在 $m$ 列中選出 $k$ 列。然後對於每一種行的選擇排列,都有 $k!$ 種列的選擇排列與其對應。

    ​ 現在我們考慮DP,令 $f_{i,j}$ 表示在 $i$ 的子樹中選出 $j$ 個點的方案數。

    ​ 我們先不考慮 $u$ 這個節點的矩陣。先考慮其兒子。顯而易見的邊界條件為:$f_{u,0}=1$ 。我們列舉他的兒子 $v$ ,那麼有轉移:$f_{u,j}=\sum\limits_{i=0}^jf_{v,i}\times f_{u,j-i}$ 。

    ​ 那麼現在 $f_{u,i}$ 就是從 $u$ 的兒子中選擇 $i$ 個點的方案數。然後把這個點也考慮上(要減去已經通過兒子選擇過的列)。於是有:$f_{u,i}=\sum\limits_{j=1}^{i}f_{u,i-j}\times \binom{siz_u-(i-j)}{j}\times\binom{a_u-a_{fa_u}}{j}\times j!$ 。

    ​ 答案就是:$f_{rt,k}$

    ​ Code:

    void DFS(int now,int fa){
        siz[now]=1;
        f[now][0]=1;
        for(int i=0;i<=1;i++){
            if(ch[now][i]==0){
                continue;
            }
            DFS(ch[now][i],a[now]);
            siz[now]+=siz[ch[now][i]];
            int v=ch[now][i];
            for(int j=min(k,siz[now]);~j;j--){
                for(int l=1;l<=min(siz[v],j);l++){
                    f[now][j]=(0ll+f[now][j]+1ll*f[v][l]*f[now][j-l]%MOD)%MOD;
                }
            }
        }
        for(int i=min(siz[now],k);~i;i--){
            for(int j=1;j<=min(i,a[now]-fa);j++){
                f[now][i]=(0ll+f[now][i]+1ll*f[now][i-j]*jc[j]%MOD*C(siz[now]-(i-j),j)%MOD*C(a[now]-fa,j)%MOD)%MOD;
                f[now][i]=(f[now][i]%MOD+MOD)%MOD;
            }
        }
    }