[BZOJ4298]-[ONTAK2015]Bajtocja-启发式合并+Hash

说在前面

me真是服气,或者me脸黑
这题Hash请用long long自然溢出不谢
反正me有一句mmp必须要讲!!


题目

BZOJ4298传送门

题面

给定 d 张无向图,每张图都有 n 个点。一开始,在任何一张图中都没有任何边。接下来有 m 次操作,每次操作会给出 a , b , k ,意为在第 k 张图中的点 a 和点 b 之间添加一条无向边。你需要在每次操作之后输出有序数对 ( a , b ) 的个数,使得 1 a , b n ,且 a 点和 b 点在 d 张图中都连通。
范围: d 200 , n 5000 , m 1000000

输入输出格式

输入格式:
第一行三个整数 d , n , m ,含义如题
接下来 m 行,每行一个三元组 ( a , b , k ) ,含义如题

输出格式:
每次操作之后,输出一行一个整数表示答案


解法

其实不难的对吧…
因为要判断多张图中,点的联通情况,保存的信息要能得出答案
不难想到使用Hash来判断,把每个点所在图与连通块哈希一下,如果两个点在所有图都联通,说明它们在每个图的联通编号都一样,这还是比较容易Hash的

然后就是维护问题了,直接用按秩合并就ok了,这样复杂度就是 m log 2 n
关于这个Hash的问题,上面已经说了….检验你们rp的时候到了!


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int D , N , M , ans , cnt ;
long long hv[7005] , Baze = 10007 , powm[205] ;
struct Path{ int pre , to ; } ;
struct HashTable{
    int head[262144] , pre[7005] , siz[7005] , que[7005] , fr , ba , tp ;
    long long val[7005] ;
    void init(){ fr = 1 , tp = ba = 0 ; }
    void Insert( long long h ){
        int id = h&262143 , t ;
        for( int i = head[id] ; i ; i = pre[i] )
            if( val[i] == h ){
                ans += siz[i] , siz[i] ++ ;
                return ;
            }
        if( ba != ( fr - 1 + 7005 )%7005 )t = que[fr] , fr = ( fr == 7004 ? 0 : fr + 1 ) ;
        else t = ++tp ;
        pre[t] = head[id] , head[id] = t ;
        val[t] = h , siz[t] = 1 ;
    }
    void Erase( long long h ){
        int id = h&262143 ;
        for( int i = head[id] , las = -1 ; i ; las = i , i = pre[i] ){
            if( val[i] == h ){
                siz[i] -- , ans -= siz[i] ;
                if( !siz[i] ){
                    ba = ( ba == 7004 ? 0 : ba + 1 ) , que[ba] = i ;
                    if( las != -1 ) pre[las] = pre[i] ;
                    else head[id] = pre[i] ;
                } return ;
            }
        }
    }
} Hs ;

struct Graph{
    Path p[10005] ;
    long long Gv ;
    int fa[5005] , siz[5005] , tp , head[5005] ;
    void In( int t1 , int t2 ){
        p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
        p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
    }
    void init(){
        cnt ++ ; powm[cnt] = powm[cnt-1] * Baze ;
        Gv = powm[cnt] ;
        for( int i = 1 ; i <= N ; i ++ ){
            fa[i] = i , siz[i] = 1 ;
            hv[i] = hv[i] + Gv * i ;
        }
    }
    int find( int x ){ return ( fa[x] == x ? x : fa[x] = find( fa[x] ) ) ; }
    void dfs( int u , int f , long long delta ){
        Hs.Erase( hv[u] ) ;
        hv[u] = hv[u] + Gv * delta ;
        Hs.Insert( hv[u] ) ;
        for( int i = head[u] ; i ; i = p[i].pre )
            if( p[i].to != f ) dfs( p[i].to , u , delta ) ;
    }
    bool Union( int a , int b ){
        a = find( a ) , b = find( b ) ;
        if( a == b ) return false ;
        if( siz[a] < siz[b] ) swap( a , b ) ;
        dfs( b , b , a - b ) ;
        In( a , b ) ;
        siz[a] += siz[b] , fa[b] = a ; return true ;
    }
} G[205] ;

void solve(){
    for( int i = 1 , a , b , k ; i <= M ; i ++ ){
        scanf( "%d%d%d" , &a , &b , &k ) ;
        G[k].Union( a , b ) ;
        printf( "%d\n" , ans * 2 + N ) ;
    }
}

int main(){
    powm[0] = 1 ;
    scanf( "%d%d%d" , &D , &N , &M ) , Hs.init() ;
    for( int i = 1 ; i <= D ; i ++ ) G[i].init() ;
    for( int i = 1 ; i <= N ; i ++ ) Hs.Insert( hv[i] ) ;
    solve() ;
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80148185