Description
Solution
首先先考虑如果限制不是区间,而是告诉你两个位置的字符相等的做法。
这样的话,我们可以把相等的位置加进一些集合里,最后答案就是(9 imes 10 ^ {num - 1}),其中(num)代表不同的集合个数,这个可以简单地用并查集维护连通性。
那么如果是两个区间的话,考虑最暴力的做法,直接把区间里对应的点加进一个集合然后按单点跑,这样肯定是过不了的,考虑优化。
首先发现区间相等这个性质满足结合律,即
[Merge(l1, r1, l2, r2) = Merge(Merge(l3, r3, l4, r4), Merge(l5, r5, l6, r6))
]
其中满足
[[l3, r3] cup [l4, r4] = [l1, r1]
]
[[l5, r5] cup [l6, r6] = [l2, r2]
]
这样的话我们就不用一个一个枚举区间里的位置加进并查集,而是可以像(st)表一样将每两个区间拆成四个(2)的幂次的形式的区间的并,将这四个区间分别加入两个并查集。
所有的区间都拆完之后我们发现现在维护连通性的都是(2)的幂长度的区间,而我们需要的是长度为(1)的区间的连通信息。这样我们可以考虑将区间下放,将每个区间拆成两个等长的区间,发现对于一个并查集里的任意一个区间,只把它和这个集合的代表元素拆开就行。
所以每一层最多下放(n)个区间,一共有(log_n)层,那么复杂度就是(O(nlog_n))的。当然这里没有考虑并查集的复杂度。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100000;
const int MOD = 1000000007;
int n, m, fa[N + 50][25], lg[N + 50], ans;
int Ksm(int a, int b)
{
int tmp = 1;
b = b % (MOD - 1);
while (b)
{
if (b & 1) tmp = 1LL * tmp * a % MOD;
a = 1LL * a * a % MOD;
b >>= 1;
}
return tmp;
}
int Find(int x, int k)
{
return fa[x][k] == x ? x : fa[x][k] = Find(fa[x][k], k);
}
void Merge(int l1, int l2, int k)
{
// cout << l1 << " " << l2 << " " << k << endl;
int fu = Find(l1, k), fv = Find(l2, k);
if (fu == fv) return;
fa[fu][k] = fv;
return;
}
void Pushdown()
{
for (int j = lg[n]; j >= 1; j--)
for (int i = 1; i + (1 << j) <= n; i++)
{
int x = Find(i, j);
if (x == i) continue;
// cout << i << " " << x << endl;
Merge(i, x, j - 1);
Merge(i + (1 << j - 1), x + (1 << j - 1), j - 1);
}
return;
}
int main()
{
scanf("%d%d", &n, &m);
lg[0] = -1;
for (int i = 1; i <= n; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= lg[n]; j++)
fa[i][j] = i;
for (int i = 1, l1, l2, r1, r2; i <= m; i++)
{
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
int length = r1 - l1 + 1;
Merge(l1, l2, lg[length]);
Merge(r1 - (1 << lg[length]) + 1, r2 - (1 << lg[length]) + 1, lg[length]);
}
Pushdown();
for (int i = 1; i <= n; i++) if (fa[i][0] == i) ans++;
printf("%d", 9LL * Ksm(10, ans - 1) % MOD);
return 0;
}