0w1

AGC D - Stamp Rally ( Parallel Binary Search OR Persistent Union-Find )

D: Stamp Rally - AtCoder Grand Contest 002 | AtCoder
Finally found an easy problem to start familiarising with parallel binary search ( I thought it is called holistic binary search ).
pekempey.hatenablog.com
The link above really helped, and basically I am just copying.
For PBS, I understand that there are at most log M < 20 depths, so we will only need to have 20 UnionFind structures declared, and with help of DFS, we ensure that when we are at some block on some depth, e.g. depth = d and range = [ ql, qr ], the edges [ 0, ql ) are united already in uf[ d ]. I mean, I thought it will be ensured by the nature of DFS. However, it seems not and one has to record the last position of that uf[ d ], and manually unite from last_pos[ d ] to mid, to assure this, which I can't comprehend yet.
For PUF, 永続セグメント木で試みたが失敗した
f:id:h0rnet:20160821164049p:plain
WA や TLE や MLE だし、諦めるしかない。。これで解いた人もなかったらしい。
Here's a great CFR post that is also a good place to begin with. Can't wait to solve Meteors this time.

struct quad{
    int first, second, third, fourth;
    quad(){}
    quad( int _f, int _s, int _t, int _fo ){
        first = _f, second = _s, third = _s, fourth = _fo;
    }
    bool operator < ( const quad &oth ) const{
        if( first != oth.first ) return first < oth.first;
        if( second != oth.second ) return second < oth.second;
        if( third != oth.third ) return third < oth.third;
        return fourth < oth.fourth;
    }
};
 
struct UnionFind{
    vi fa, size;
    UnionFind( int sz = 0 ){
        fa.resize( sz );
        size.resize( sz );
        for( int i = 0; i < sz; ++i )
            fa[ i ] = i,
            size[ i ] = 1;
    }
    int find( int x ){
        if( fa[ x ] == x ) return x;
        return fa[ x ] = find( fa[ x ] );
    }
    bool same( int x, int y ){
        int a = find( x );
        int b = find( y );
        return a == b;
    }
    bool unite( int x, int y ){
        int a = find( x );
        int b = find( y );
        if( a == b ) return false;
        fa[ a ] = b;
        size[ b ] += size[ a ];
        return true;
    }
} uf[ 20 ];
 
int pos[ 20 ];
 
int N, M;
vector< pii > edge;
 
void parallel_bs( int dpt, int ql, int qr, vector< quad > cur, vi &ans ){
    if( ql == qr ){
        for( auto q : cur )
            ans[ q.fourth ] = ql;
        return;
    }
    int mid = ql + qr >> 1;
    for( int &i = pos[ dpt ]; i <= mid; ++i )
        uf[ dpt ].unite( edge[ i ].first, edge[ i ].second );
    vector< quad > left, right;
    for( auto q : cur ){
        int a = uf[ dpt ].find( q.second );
        int b = uf[ dpt ].find( q.third );
        int size = uf[ dpt ].size[ a ] + uf[ dpt ].size[ b ];
        if( a == b ) size /= 2;
        if( size >= q.first ) left.push_back( q );
        else right.push_back( q );
    }
    parallel_bs( dpt + 1, ql, mid, left, ans );
    parallel_bs( dpt + 1, mid + 1, qr, right, ans );
}
 
void solve(){
    cin >> N >> M;
 
    edge = vector< pii >( M );
    for( int i = 0; i < M; ++i )
        cin >> edge[ i ].first >> edge[ i ].second,
        --edge[ i ].first, --edge[ i ].second;
 
    int Q; cin >> Q;
    vector< quad > qry( Q );
    for( int i = 0; i < Q; ++i )
        cin >> qry[ i ].second >> qry[ i ].third >> qry[ i ].first,
        --qry[ i ].second, --qry[ i ].third,
        qry[ i ].fourth = i;
 
    for( int i = 0; i < 20; ++i )
        uf[ i ] = UnionFind( N );
 
    vi ans( Q );
    parallel_bs( 0, 0, M - 1, qry, ans );
 
    for( int i = 0; i < Q; ++i )
        cout << ans[ i ] + 1 << "\n";
}
// persistent segment tree for persistent union find -> WA + TLE + MLE QQ
#include <bits/stdc++.h>
using namespace std;

typedef pair< int, int > pii;
typedef vector< int > vi;
typedef vector< vi > vvi;
typedef vector< pii > vp;
typedef vector< vp > vvp;

const int MAXN = 1e5 + 1;
const int MAXM = 1e5 + 1;
const int MAXQ = 1e5 + 1;

const int MAXMEM = 4e6;

struct itvt_data{
    int fa, size;
    itvt_data( int _f = -1, int _s = 1 ): fa( _f ), size( _s ){}
};

struct itvt{
    static itvt mem[ MAXMEM ];
    int lb, rb;
    itvt_data data;
    itvt *lc, *rc;
    itvt(): lc( NULL ), rc( NULL ){}
    itvt( int _l, int _r, int _f ): lb( _l ), rb( _r ){
        data.fa = _f;
    }
    itvt_data get_data( int k ){ // returns fa, log( rb - lb )
        if( lb == rb ) return data;
        int mid = ( lb + rb ) / 2;
        if( k <= mid ) return lc->get_data( k );
        return rc->get_data( k );
    }
    void update( int k, const itvt_data &d ){
        if( lb == rb ) return ( void )( data = d );
        int mid = ( lb + rb ) / 2;
        if( k <= mid ) return lc->update( k, d );
        return rc->update( k, d );
    }
    int find( int x ){
        int fa_x = get_data( x ).fa;
        if( x == fa_x ) return x;
        update( x, itvt_data( fa_x, 0 ) );
        return fa_x;
    }
} itvt::mem[ MAXMEM ], *ptr = itvt::mem, *ver[ MAXM ];

itvt* build( int lb, int rb ){
    if( lb == rb ) return new ( ptr++ ) itvt( lb, rb, lb );
    int mid = ( lb + rb ) / 2;
    itvt *t = new ( ptr++ ) itvt( lb, rb, -1 );
    t->lc = build( lb, mid );
    t->rc = build( mid + 1, rb );
    return t;
}

itvt* clone( itvt *t ){
    itvt *res = new ( ptr++ ) itvt();
    res->lb = t->lb, res->rb = t->rb, res->data = t->data;
    res->lc = t->lc, res->rc = t->rc;
    return res;
}

itvt* modify( itvt *t, int k, const itvt_data &d ){
    itvt *res = clone( t );
    if( t->lb == t->rb ){
        res->data = d;
        return res;
    }
    int mid = ( t->lb + t->rb ) / 2;
    if( k <= mid ) res->lc = modify( t->lc, k, d );
    else res->rc = modify( t->rc, k, d );
    return res;
}

itvt* unite( itvt *t, int u, int v ){
    int a = t->find( u );
    int b = t->find( v );
    if( a == b ) return clone( t );
    int size_a = t->get_data( a ).size;
    int size_b = t->get_data( b ).size;
    itvt *res = modify( t, a, itvt_data( b, size_b ) );
    res = modify( res, b, itvt_data( b, size_a + size_b ) );
    // res->update( a, itvt_data( b, size_a ) );
    // res->update( b, itvt_data( b, size_a + size_b ) );
    return res;
}

void solve(){
    int N, M; cin >> N >> M;
    ver[ 0 ] = build( 0, N - 1 );

    for( int i = 1; i <= M; ++i ){
        int u, v; cin >> u >> v;
        --u, --v;
        ver[ i ] = unite( ver[ i - 1 ], u, v );
    }

    int Q; cin >> Q;
    for( int i = 0; i < Q; ++i ){
        int u, v, c; cin >> u >> v >> c;
        --u, --v;
        int lb = 1, rb = M, ans = -1;
        while( lb <= rb ){
            int mid = lb + rb >> 1;
            int a = ver[ mid ]->find( u );
            int b = ver[ mid ]->find( v );
            int size = ver[ mid ]->get_data( a ).size +
                       ver[ mid ]->get_data( b ).size;
            if( a == b ) size /= 2;
            if( size >= c ) ans = mid, rb = mid - 1;
            else lb = mid + 1;
        }
        cout << ans << "\n";
    }
}

int main(){
    ios::sync_with_stdio( false );
    solve();
    return 0;
}