bzoj4569 [Scoi2016]萌萌噠
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\)
答案模 \(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{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]萌萌噠