【Codeforcesの603e]田園倒錯変態(LCT)

【Codeforcesの603e]田園倒錯変態(LCT)

フェイス質問

図は、n個の点、順次無向図面の右側に追加m個のバンドが存在する単離されました。各エッジのセットポイントが奇数であることを、図度を最大重み値設定エッジの重みとして定義され、正当なものです。エッジの各添加後、右呼び掛け最小法定重量エッジ設定値、法定の出力側のセットは、-1が存在しません。

\(N \当量10 ^ 5、M \ 3当量\回10 ^ 5 \)

分析

利用可能なハンドプレイサンプル:有効エッジ集合の存在下、各ブロックのサイズが偶数の通信である場合にのみ

証明:

充足を証明する:正当な通信ブロックサイズが奇数であると仮定し、そのブロックは、総度の奇数です。しかし、全ての点と偶数のある程度は(片面2点度カウントされるため)。だから、セットの法的側面は存在しません。したがって、通信ブロックサイズは偶数でなければなりません。

そして、正当化:任意のスパニングツリー通信ブロックを得ました。各非ルートノードのボトムアッププロセス(X \)\。もし\(X \)そのスパニングツリー子側に、選出されたエッジの組の偶数を有します。次に\(X \)\(FA(X)\)ように、エッジのセット側に追加される(X \)\次数が奇数です。ルートノードのために、我々は、非ルートノード度が奇数のすべてのセットが、すべての点度の数であり、偶数入れているという理由だけで、その程度の根は奇数でなければなりません。

十分な注意事項の証明は、回答コンストラクタを与えられています。この方法は、元の通信ブロックにまたがるエッジを選択しなければなりません。そして、元のブロックを通信する小さなブロックの通信に分割される(場合(X \)\ではない接続に)\)\(FA(X)、各ブロックの最小スパニングツリーが正当な新しいユニコムの最小量でありますサイドセット。

我々は小から大まで重量で、森に各エッジを維持するために、プライオリティキューを使用しています。実現のためにpriority_queueいくつかの制限の、我々は切り替えset< pair<int,int> >。最初のキーを右側に、キーワードIDの第2の面を

その後、我々は、LCTは、スパニングツリーの森を維持することができます。

各縁取り作業を考えてみましょう\((U、V、W )\) 効果。

  1. 場合(Uは、V \)\接続されていた、そして私たちは、探し最小全域木を維持する\(U \)への\(V \)パスの最大の右側にある\(W 0 \)

    場合(\ W> 0 W)\、その後、スパニングツリーの上に置く(W_0 \)\(\ w)が\。同時に更新set

    、エッジの重みの最大出力可解集合(度の奇数がある場合ユニコムブロック)そうでなければ、現在の解決できるかどうかを決定します。

  2. 場合(U、V \)\通信していない、我々は新しいエッジとLCTを追加しましたset。ユニコムと奇数ブロック度の数を更新します。

  3. スパニングツリーの変更のエッジは、私たちが必要な場合は、最適な答えを再計算します。set最大のエッジスタート、このエッジの除去かどうかを確認します。私たちは、チェックを削除することができた場合は削除され、二番目に大きい、それ以外の場合は終了されます。

そして、基本的な操作に対する当社のLCTの追加は、サポートします:

  1. 最大の側の上にクエリパス右。同様に[ BZOJ2594] [WC2006]秘書水(クラスカル+ LCT)を検討する点として。

  2. クエリサブツリースパニングツリー上の点の大きさ。同様【BJOI2014]大統合(リンクカットツリー)仮想サブツリー情報のメンテナンスをすることができます。私たちがポイントになることを見ているので注意が、ポイント(P \)\サブツリーは、LCTになりました\( - 1)= 2P-1 \ P-+(P) その後、実際の点はである\((SZ [P] +1 )/ 2 \)

複雑分析:操作のLCT複雑である\(O((N-M +)\ Nログ)\) 各エッジは、一度だけセット内の複雑度を除去する(O(M \ログM)\)\

概要:これは、スパニングツリーの問題への問題の性質の素晴らしいタイトル最初に使用され、その後、LCTは、スパニングツリーを維持し、両方のサブルーチンに関する情報を保持し兼ね備えています。難しいの両方のデータ構造の難易思考を持っています。人々は、タイトルの親指を与えています!

コード

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<utility>
#define maxn 100000
#define maxm 300000
using namespace std;
int n,m;
struct edge {
    int from;
    int to;
    int len;
} E[maxm+5];
 
 
struct LCT {
#define fa(x) (tree[x].fa)
#define lson(x) (tree[x].ch[0])
#define rson(x) (tree[x].ch[1])
    struct node {
        int ch[2];
        int fa;
        int sz;//原树上的子树大小
        int vsz;//虚子树大小
        int id;
        int maxid;
        int revm;
    } tree[maxn+maxm+5];
    inline bool is_root(int x) {
        return !(lson(fa(x))==x||rson(fa(x))==x);
    }
    inline int check(int x) {
        return rson(fa(x))==x;
    }
    void push_up(int x) {
        tree[x].sz=tree[lson(x)].sz+1+tree[rson(x)].sz+tree[x].vsz;
        tree[x].maxid=tree[x].id;
        if(E[tree[lson(x)].maxid].len>E[tree[x].maxid].len) tree[x].maxid=tree[lson(x)].maxid;
        if(E[tree[rson(x)].maxid].len>E[tree[x].maxid].len) tree[x].maxid=tree[rson(x)].maxid;
    }
    void reverse(int x) {
        swap(lson(x),rson(x));
        tree[x].revm^=1;
    }
    void push_down(int x) {
        if(tree[x].revm) {
            reverse(lson(x));
            reverse(rson(x));
            tree[x].revm=0;
        }
    }
    void push_down_all(int x) {
        if(!is_root(x)) push_down_all(fa(x));
        push_down(x);
    }
    void rotate(int x) {
        int y=fa(x),z=fa(y),k=check(x),w=tree[x].ch[k^1];
        tree[y].ch[k]=w;
        tree[w].fa=y;
        if(!is_root(y)) tree[z].ch[check(y)]=x;
        tree[x].fa=z;
        tree[x].ch[k^1]=y;
        tree[y].fa=x;
        push_up(y);
        push_up(x);
    }
    void splay(int x) {
        push_down_all(x);
        while(!is_root(x)) {
            int y=fa(x);
            if(!is_root(y)) {
                if(check(x)==check(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        push_up(x);
    }
    void access(int x) {
        for(int y=0; x; y=x,x=fa(x)) {
            splay(x);
            tree[x].vsz-=tree[y].sz-tree[rson(x)].sz;//把原来是虚子树,现在是实的减掉
            rson(x)=y;
            push_up(x);
        }
    }
    void make_root(int x) {
        access(x);
        splay(x);
        reverse(x);
    }
    void split(int x,int y) {
        make_root(x);
        access(y);
        splay(y);
    }
    void link(int x,int y) {
        make_root(x);
        fa(x)=y;
        tree[y].vsz+=tree[x].sz;
        push_up(y);
    }
    void cut(int x,int y) {
        split(x,y);
        fa(x)=lson(y)=0;
        push_up(y);
    }
    void add_edge(int id) {
        link(E[id].from,id+n);
        link(E[id].to,id+n);
    }
    void del_edge(int id) {
        cut(E[id].from,id+n);
        cut(E[id].to,id+n);
        tree[id+n].vsz=0;
    }
    int find_root(int x) {
        access(x);
        splay(x);
        while(lson(x)) x=lson(x);
        return x;
    }
 
    int query_route(int x,int y) {
        split(x,y);
        return tree[y].maxid;
    }
} T;
set< pair<int,int> >ed;//按边权从小到大存储生成树中的边,first为边权,second为编号
inline int is_odd(int x) {
    //判断x所在联通块的点数是否为奇数
    //LCT里p个点,p-1条边,sz[x]=2p-1
    //真正的点数为(sz[x]+1)/2
    return ((T.tree[x].sz+1)/2)%2==1;
}
void build() {
    while(1) {
        int id=(--ed.end())->second;
        int x=E[id].from;
        int y=E[id].to;
        T.split(x,y);
        if(is_odd(x)) break;
        T.del_edge(id);
        ed.erase(*(--ed.end()));
    }
}
int main() {
    scanf("%d %d",&n,&m);
    for(int i=1; i<=n+m; i++) {
        if(i<=n) T.tree[i].id=0;
        else T.tree[i].id=i-n;
        T.push_up(i);
    }
    for(int i=1; i<=m; i++) scanf("%d %d %d",&E[i].from,&E[i].to,&E[i].len);
    int odd_sz_cnt=n;
    for(int i=1; i<=m; i++) {
        int u=E[i].from,v=E[i].to,w=E[i].len;
        if(T.find_root(u)==T.find_root(v)) {
            int now=T.query_route(u,v);
            if(w<E[now].len) {
                T.del_edge(now);
                T.add_edge(i);
                ed.erase(make_pair(E[now].len,now));
            } else {
                if(odd_sz_cnt>1) printf("-1\n");
                else printf("%d\n",(--ed.end())->first);//输出最大边
                continue;
            }
        } else {
            T.make_root(u);
            odd_sz_cnt-=is_odd(u);
            T.make_root(v);
            odd_sz_cnt-=is_odd(v);
            //把原来的两个联通块的贡献减掉
            T.add_edge(i);
            T.make_root(u);
            odd_sz_cnt+=is_odd(u);
            //再加回新的贡献
        }
        ed.insert(make_pair(w,i));
        if(odd_sz_cnt>0) {
            printf("-1\n");
            continue;
        }
        build();//构造出联通块里选择的边集
        printf("%d\n",(--ed.end())->first);
    }
}

おすすめ

転載: www.cnblogs.com/birchtree/p/12207725.html