1. 程式人生 > >bzoj4569 [Scoi2016]萌萌噠

bzoj4569 [Scoi2016]萌萌噠

lag down c++ digi IT 處理 答案 AD 不同

Description

\(\mathrm{bzoj}\) 上的題面太毒瘤了,重寫一下。

一個長度為 \(n\) 的大數,用 \(S_1,S_2,S_3\cdots 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}\cdots S_{r_1}\)\(S_{l_2}, S_{l_2+1},S_{l_2+2}\cdots S_{r_2}\) 完全相同。比如 \(n=6\)

時,某限制條件 \(l_1=1,r_1=3,l_2=4,r_2=6\) ,那麽 \(123123\)\(351351\) 均滿足條件,但是 \(12012\)\(131141\) 不滿足條件,前者數的長度不為 \(6\) ,後者第二位與第五位不同。問滿足以上所有條件的數有多少個。

答案模 \(10^9+7\)

\(1\le n,m\le 10^5,r_1-l_1=r_2-l_2\)

Solution

好題!這題的做法太 \(\mathrm{tm}\) 妙了!我被 \(\mathrm{vanillayi}\) 巨巨吊打!我天天被吊打!

我們先來考慮暴力怎麽做(菜雞 \(\mathrm{azi}\)

連暴力都寫錯)。顯然相等關系可以用並查集來維護,就像這樣:

while(m--) {
    int l1 = read(), r1 = read(), l2 = read(), r2 = read(), len = r1 - l1 + 1;
    rep(i, 0, len - 1) merge(l1 + i, l2 + i);
}

最後答案就是 \(9\times 10^{集合個數 - 1}\)

但是這樣是 \(nm\) 的,會掛。但是花爸爸說這玩意兒可以卡過去。

我們面向數據範圍編程可以猜測,這道題的算法應該是 \(m\log n\) 之類的什麽玩意兒。

線段樹?好像沒法合並。

於是我們

\(\mathrm{vanillayi}\) 大爺想到了倍增。

我們使用一個奇妙的科技叫做倍增並查集,有點類似於 \(\mathrm{ST}\) 表。

\(fa[x][y]\) 表示 \(\left[x,x+2^y-1\right]\) 這一塊的“父親”是誰。

對於第 \(i\) 個限制,設 \(k=\left\lfloor \log_2len_i \right\rfloor\) 那麽我們就可以將 \([l_{1_i},l_{1_i}+2^k-1]\) 這一塊的父親置為 \([l_{2_i},l_{2_i}+2^k-1]\) ,將 \([r_{1_i}-2^k+1,r_{1_i}]\) 的父親置為 \([r_{2_i}-2^k+1,r_{2_i}]\) ,遞歸處理內部。

統計答案同暴力,掛一個快速冪。

#include<bits/stdc++.h>
using namespace std;

#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define P 1000000007
#define ll long long

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

int n, m, fa[N][17];

int find(int x, int y) { return x == fa[x][y] ? x : fa[x][y] = find(fa[x][y], y); }
void merge(int x, int y, int k) {
    int fx = find(x, k), fy = find(y, k); if (fx == fy) return;
    fa[fx][k] = fy;
    if (k--) merge(x, y, k), merge(x + (1 << k), y + (1 << k), k);
}
inline ll quickPow(ll a, int k) { ll ret = 1; for (; k; k >>= 1, (a *= a) %= P) if (k & 1) (ret *= a) %= P; return ret; }
int main() {
    n = read(), m = read();
    rep(i, 1, n) rep(j, 0, 17) fa[i][j] = i;
    while (m--) {
        int l1 = read(), r1 = read(), l2 = read(), r2 = read(), k = 0;
        while ((1 << k) <= r1 - l1 + 1) k++; k--;
        merge(l1, l2, k), merge(r1 - (1 << k) + 1, r2 - (1 << k) + 1, k);
    }
    int cnt = -1; rep(i, 1, n) cnt += (fa[i][0] == i);
    printf("%lld", 9ll * quickPow(10, cnt) % P);
    return 0;
}

bzoj4569 [Scoi2016]萌萌噠