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\) 时,某限制条件 \(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;
}
posted @ 2018-03-10 22:42  aziint  阅读(155)  评论(0编辑  收藏  举报
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.