1. 程式人生 > 實用技巧 >[SCOI2016]萌萌噠

[SCOI2016]萌萌噠

看上去毫無思路感覺不可做,可以先考慮暴力。

不難發現如果將整個區間一起考慮十分不好做,我們可以考慮對每一位進行限制,於是對於每個限制,我們將要求相同的每個位置加入並查集,那麼最終答案就之和連通塊個數有關了。

下面考慮優化這個暴力的過程,可以發現單獨考慮每一位很好做是因為這些需要限制的區間是已知且能表示出來的,那麼什麼東西能以優秀複雜度表示一段區間且能預處理呢?沒錯,就是倍增。具體來說,對於每一條限制,我們從 \(l1, l2\) 開始倍增地跳,將倍增長度相同的線段在並查集中合併,而在最後查詢的時候我們依然需要將限制放到每一位去,可以考慮將倍增長度為 \(2 ^ i\) 的線段往下拆分成 \(2 ^ {i - 1}\)

的線段,只需將該長度連通塊內左半邊合併,右半邊合併即可。為了不讓線段之間兩兩合併複雜度爆炸,我們只需考慮拆分一條線段和它在並查集上的父親併合並即可。

從這題可以看出來倍增使用條件有下面幾條:

  • 沒有修改,只有查詢。

  • 資訊滿足可減,可加性。

因此在沒有修改情況下,我們一般可以考慮使用倍增。

#include<bits/stdc++.h>
using namespace std;
#define N 2000000 + 5
#define Mod 1000000007
#define rep(i, l, r) for(int i = l; i <= r; ++i)
#define dep(i, l, r) for(int i = r; i >= l; --i)
int n, m, l1, r1, l2, r2, cnt, fa[N];
int read(){
    char c; int x = 0, f = 1;
    c = getchar();
    while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int c(int x, int y){
    return x + y * n;
}
int Inc(int a, int b){
    return (a += b) >= Mod ? a - Mod : a;
}
int Mul(int a, int b){
    return 1ll * a * b % Mod;
}
int Qpow(int a, int b){
    int ans = 1;
    while(b){
        if(b & 1) ans = Mul(ans, a);
        a = Mul(a, a), b >>= 1;
    }
    return ans;
}
int find(int x){
    return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
void Merge(int x, int y){
    int a = find(x), b = find(y);
    if(a == b) return;
    fa[a] = b;
}
void solve(int l1, int r1, int l2, int r2){
    int x = l1, y = l2;
    dep(i, 0, 17) if(x + (1 << i) - 1 <= r1){
        Merge(c(x, i), c(y, i));
        x = x + (1 << i), y = y + (1 << i);
    }
}
int main(){
    n = read(), m = read();
    rep(j, 0, 17) rep(i, 1, n) fa[c(i, j)] = c(i, j);
    rep(i, 1, m){
        l1 = read(), r1 = read(), l2 = read(), r2 = read();
        solve(l1, r1, l2, r2);
    }
    dep(j, 1, 17) rep(i, 1, n) if(fa[c(i, j)] != c(i, j)){
        int x = i, y = fa[c(i, j)] % n == 0 ? n : fa[c(i, j)] % n;
        Merge(c(x, j - 1), c(y, j - 1));
        if(x + (1 << (j - 1)) <= n && y + (1 << (j - 1)) <= n)
            Merge(c(x + (1 << (j - 1)), j - 1), c(y + (1 << (j - 1)), j - 1));
    }
    rep(i, 1, n) if(fa[i] == i) ++cnt;
    printf("%d", Mul(9, Qpow(10, cnt - 1)));
    return 0;
}