1. 程式人生 > >「SCOI2016」萌萌噠

「SCOI2016」萌萌噠

之間 block pre 但是 由於 所在 its 其中 return

「SCOI2016」萌萌噠

題目描述

一個長度為 \(n\) 的大數,用 \(S_1S_2S_3 \ldots S_n\) 表示,其中 \(S_i\) 表示數的第 \(i\) 位,\(S_1\) 是數的最高位,告訴你一些限制條件,每個條件表示為四個數 $(l_1, r_1, l_2, r_2) $,即兩個長度相同的區間,表示子串 $S_{l_1}S_{l_1 + 1}S_{l_1 + 2} \ldots S_{r_1} $與 \(S_{l_2}S_{l_2 + 1}S_{l_2 + 2} \ldots S_{r_2}\)完全相同。

比如 \(n = 6\) 時,某限制條件 $(l_1 = 1, r_1 = 3, l_2 = 4, r_2 = 6) $,那麽 \(123123\)

\(351351\) 均滿足條件,但是 \(12012\)\(131141\) 不滿足條件,前者數的長度不為 \(6\),後者第二位與第五位不同。問滿足以上所有條件的數有多少個。

\(1 \leq n \leq 10^5, 1 \leq m \leq 10^5,1 \leq li_1,ri_1,li_2,ri_2 \leq n\) 並且保證 ${r_i}_1 - {l_i}_1 = {r_i}_2 - {l_i}_2 $

解題思路 :

觀察發現,限制條件的本質是使得一些位置只能填同樣的字符,我們不妨把限制在一起的位置看做點,在它們之間連一條邊

那麽統一聯通塊裏面的位置就只能填同一字符了,所以設聯通快數為 \(x\)

,那麽答案就是 \(9 \times 10^{x-1}\) (第一位不能填 \(0\) ,所以少一種選擇)

於是就有一個暴力的做法,用並查集維護聯通塊,每次對於一組限制 \(l_1, r_1, l_2, r_2\) ,暴力將兩個區間的對應點合並,復雜度 \(O(n^2logn)\)

考慮怎麽優化並查集的合並,由於是區間問題,所以很容易就想到用線段樹或者 \(st\) 表來維護

每次把可以詢問區間拆成 \(log\) 個區間,區間與區間之間進行連邊,難點在於最後怎麽將區間之間的合並轉化到點上

由於題目只需要最終詢問一次,不妨利用 \(lazytag\) 的思想,對每一種長度的區間用一個並查集來維護,最後算答案的時候將合並信息下傳

具體來講,考慮 \(st\) 表的做法:設 \(fa(i,j)\) 表示左端點為 \(i\) 的長度為 \(2 ^ j\) 的區間所在集合的 \(root\) 的左端點

那麽下傳信息的時候只需要 \((i, j-1), (i+2^{j-1},j-1)\) 分別和 \((fa(i, j), j - 1)\) 合並即可,最後統計一下 \((i, 0)\) 的聯通塊數 ,總復雜度 \(O(nlog^2n)\)


/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}

const int N = 500005, Mod = 1000000007;
int fa[N][21], n, m;

inline int ask(int x, int y){ 
    return x == fa[x][y] ? x : fa[x][y] = ask(fa[x][y], y);
}

int main(){
    read(n), read(m);
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= 20; j++) fa[i][j] = i;  
    
    while(m--){
        int l1, r1, l2, r2;
        read(l1), read(r1), read(l2), read(r2);
        int ps1 = l1, ps2 = l2;
        for(int i = 20; ~i; i--) 
            if(ps1 + (1 << i) - 1 <= r1){
                int p = ask(ps1, i), q = ask(ps2, i);
                if(p != q) fa[p][i] = q;
                ps1 += (1 << i), ps2 += (1 << i);
            }
    }
    for(int j = 20; j; j--)
        for(int i = 1; i + (1 << j) - 1 <= n; i++){
            int p = ask(i, j), q = ask(i, j - 1);
            fa[q][j-1] = ask(p, j - 1);
            p = ask(i, j), q = ask(i + (1 << j - 1), j - 1);
            fa[q][j-1] = ask(p + (1 << j - 1), j - 1);
        }
    
    ll res = 1ll;
    for(int i = 1; i <= n; i++)
        if(fa[i][0] == i) (res *= (res == 1ll) ? 9ll : 10ll) %= Mod;
    cout << res; 
    return 0;
}

「SCOI2016」萌萌噠