@bzoj - 4298@ [ONTAK2015]Bajtocja


@description@

給定d張無向圖,每張圖都有n個點。一開始,在任何一張圖中都沒有任何邊。接下來有m次操做,每次操做會給出a,b,k,意爲在第k張圖中的點a和點b之間添加一條無向邊。你須要在每次操做以後輸出有序數對(a,b)的個數,使得1<=a,b<=n,且a點和b點在d張圖中都連通。優化

Input
第一行包含三個正整數d,n,m(1<=d<=200,1<=n<=5000,1<=m<=1000000),依次表示圖的個數,點的個數和操做的個數。
接下來m行,每行包含三個正整數a,b,k(1<=a,b<=n,1<=k<=d),依次描述每個操做。spa

Output
輸出m行m個正整數,依次表示每次操做以後知足條件的有序數對(a,b)的個數。code

Sample Input
3 4 10
1 2 1
2 1 2
1 2 3
3 4 1
1 3 2
2 3 3
2 4 2
3 4 3
3 4 2
1 3 1
Sample Output
4
4
6
6
6
6
6
8
8
16ip

@solution@

考慮動態統計增量。
當合並兩個連通塊時,咱們採用啓發式合併的方法,將小的一個個塞進大的裏面。在一個個塞的時候,統計新多出來的在 d 張圖都連通的點對數量。
由於是啓發式合併,每一個點最多被暴力塞 log 次,因此這一部分的複雜度爲 O(nlogn)。字符串

怎麼判斷兩個點在 d 張圖中都連通呢?由於只有加邊,考慮使用並查集。
假如對於點 x 與點 y 在每張圖中都有 find(x) = find(y)(find(x) 即 x 所在集合的表明元素),那麼在 d 張圖中 x, y 都連通。
在求點 x 與多少個點在 d 張圖中都連通,即給定一個 find 序列 find(1,x), find(2,x), ..., find(d,x),求有多少個點對應的 find 序列與它相同。get

斷定兩個序列是否相等,除了逐個比較外還能夠使用字符串哈希的方法。
那麼就能夠經過將 find 序列給哈希了,獲得的哈希值拿去找相同的值有多少個。
這個能夠再寫一個哈希表搞定(注意哈希表 ≠ 字符串哈希,雖然都是哈希。。。)hash

在合併連通塊的時候,find 序列只會有一個位置發生改變,改哈希值能夠 O(1) 改。it

@accepted code@

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ull, int> hh;
const int MAXD = 200;
const int MAXN = 5000;
const ull BASE = 13131;
const int HASHSIZE = 1000037;
vector<hh>h[HASHSIZE];
int ans;
void hash_insert(ull x) {
    int hx = x % HASHSIZE;
    for(int i=0;i<h[hx].size();i++)
        if( h[hx][i].fi == x ) {
            ans += 2*h[hx][i].se;
            h[hx][i].se++;
            return ;
        }
    h[hx].push_back(make_pair(x, 1));
}
void hash_erase(ull x) {
    int hx = x % HASHSIZE;
    for(int i=0;i<h[hx].size();i++)
        if( h[hx][i].fi == x ) {
            h[hx][i].se--;
            ans -= 2*h[hx][i].se;
        }
}
ull pw[MAXD + 5], hsh[MAXN + 5];
vector<int>sn[MAXD + 5][MAXN + 5];
int fa[MAXD + 5][MAXN + 5], rnk[MAXD + 5][MAXN + 5];
int find(int d, int x) {
    return fa[d][x] = (fa[d][x] == x ? x : find(d, fa[d][x]));
}
void dfs(int d, int x, ull del) {
    for(int i=0;i<sn[d][x].size();i++)
        dfs(d, sn[d][x][i], del);
    hash_erase(hsh[x]);
    hsh[x] += del;
    hash_insert(hsh[x]);
}
void unite(int d, int x, int y) {
    x = find(d, x), y = find(d, y);
    if( x == y ) return ;
    if( rnk[d][x] < rnk[d][y] ) swap(x, y);
    dfs(d, y, pw[d]*x - pw[d]*y);
    fa[d][y] = x, sn[d][x].push_back(y);
    rnk[d][x] += rnk[d][y];
}
int d, n, m;
int read() {
    int x = 0; char ch = getchar();
    while( ch > '9' || ch < '0' ) ch = getchar();
    while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    return x;
}
int main() {
    d = read(), n = read(), m = read();
    pw[1] = 1;
    for(int i=2;i<=d;i++)
        pw[i] = pw[i-1]*BASE;
    for(int i=1;i<=d;i++)
        for(int j=1;j<=n;j++) {
            fa[i][j] = j, rnk[i][j] = 1;
            hsh[j] += j*pw[i];
        }
    for(int i=1;i<=n;i++) hash_insert(hsh[i]);
    for(int i=1;i<=m;i++) {
        int a = read(), b = read(), k = read();
        unite(k, a, b), printf("%d\n", ans + n);
    }
}

@details@

讀入優化真好用.jpg。io

注意題目中說的是有序數對啊。

相關文章
相關標籤/搜索