题目
一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条
件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...S
r2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,13
1141不满足条件,前者数的长度不为6,后者第二位与第五位不同。问满足以上所有条件的数有多少个。
Input
第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。接下来m行,对于第i行,有4个数li1,ri1,li2
,ri2,分别表示该限制条件对应的两个区间。
1≤n≤105,1≤m≤105,1≤li1,ri1,li2,ri2≤n;并且保证ri1-li1=ri2-li2。
Output
一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模10^9+7的结果即可。
Sample Input
4 2
1 2 3 4
3 3 3 3
Sample Output
90
Solve
-
暴力的话就是对于每个区间都扫一下,然后并查集合并,最后看有几个并查集,由于每个并查集里的数都得是一样的,第一位有9种选法,之后的都有10种,乘起来就好了
-
时间复杂度nm,接受不了,考虑优化
-
运用倍增的思想,k,x表示的从x开始的2的k次方的区间内的所有点,合并的时候就可以log的合并
-
最后把合并下传下去就好了,挺妙的
-
合并的时候我用的二进制拆分,其实用st表那种合并方式也行,而且会更快
Code
#include <cstdio>
using namespace std;
const int N = 1e5 + 5, M = 1e9 + 7;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
bool g;
int n, m, f[20][N], ans;
int Find(int i, int x) {
return x == f[i][x] ? x : f[i][x] = Find(i, f[i][x]);
}
void Merge(int i, int x, int y) {
f[i][Find(i, x)] = Find(i, y);
}
int main() {
n = read(); m = read();
for (int i = 0; i <= 16; ++i)
for (int x = n - (1 << i) + 1; x >= 1; --x)
f[i][x] = x;
while (m--) {
int l1 = read(), r1 = read(), l2 = read(), r2 = read();
for (int i = 16; i >= 0; --i)
if (l1 + (1 << i) - 1 <= r1)
Merge(i, l1, l2), l1 += 1 << i, l2 += 1 << i;
}
for (int i = 16; i >= 1; --i) {
for (int x = n - (1 << i) + 1; x >= 1; --x) {
int fx = Find(i, x);
if (fx == x) continue;
Merge(i - 1, x, fx);
Merge(i - 1, x + (1 << i - 1), fx + (1 << i - 1));
}
}
for (int i = 1; i <= n; ++i) {
if (i != Find(0, i)) continue;
if (!g) ans = 9, g = 1;
else ans = 10ll * ans % M;
}
printf("%d
", ans);
return 0;
}