题目
题目描述
有一条数字长链,这条链上有些数字是相同的,有些地方是不同的,这条数链有很多种不同的可能。求最多有多少种可能的方案?
为了简化题目,数链没有前导零。因为答案可能很大,所以你只需要输出答案
m
o
d
1
0
9
+
7
\mod10^9+7
mod109+7的结果。
输入输出格式
输入格式
第一行:两个正整数
N
(
1
≤
N
≤
100000
)
N(1\le N\le 100000)
N(1≤N≤100000)和
M
(
1
≤
M
≤
100000
)
M(1\le M \le 100000)
M(1≤M≤100000),表示这个数的长度为
N
N
N,提出了
M
M
M个要求。
接下来
M
M
M行:每行四个整数
l
1
,
r
1
,
l
2
,
r
2
l_1,r_1,l_2,r_2
l1,r1,l2,r2,表示该数链的
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1]与
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2]相同。
输出格式
仅一行,一个整数,有多少种可能的情况。
思路
这两个相同的、长度为
l
e
n
len
len的区间,可以拆分为两个区间分别相等。
是否在哪里看过这种一个区间拆成两个区间的、高效的,类似的呢?——
R
M
Q
\Bbb{RMQ}
RMQ!
这提示我们第二维
j
j
j表示区间长度为
2
j
2^j
2j。这样,就可以用 并查集 来存储每一个相同的了!
具体来说:将有序数对
(
i
,
j
)
(i,j)
(i,j)当做下标,存储其并查集中的父节点。
而父节点与子节点的长度相同,只需存储父节点的
first
\text{first}
first。
统计答案时,最终所有
second
\text{second}
second不为
0
0
0的(也就是长度不为
2
0
=
1
2^0=1
20=1的),都要下传成长度为
1
1
1的(便于统计)。
那么我们一开始就传下去就好啦!有一种情况可以中途剪枝:想要固定相同的两个区间本来就 因为各种不可描述的原因 相同。
代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long LL;
const int LG = 18, Max = 100000, MOD = 1e9 + 7;
int N, M, Lg[Max+5], f[Max+5][LG];
bool vis[Max+5];
int findRoot(int x,int t){ // 并查集找根
if(not f[x][t])
return x; // 路径压缩
return f[x][t] = findRoot(f[x][t],t);
}
void Merge(int a,int b,int t){
int x = findRoot(a,t);
int y = findRoot(b,t);
if(x == y) return ; // 已经要求相同
f[x][t] = y;
if(not t) return ; // 最底层
Merge(a,b,t-1);
Merge(a+(1<<t>>1),b+(1<<t>>1),t-1);
}
int main(){
cin >> N >> M;
Lg[0] = -1, Lg[1] = 0;
for(int i = 2; i <= N; ++ i)
Lg[i] = Lg[i>>1]+1;
for(int j=0; j<=Lg[N]; ++j)
for(int i=1; i<=N; ++i)
f[i][j] = 0; // 根节点
int a, b, c, d;
for(int i = 1; i <= M; ++ i){
cin >> a >> b >> c >> d;
int t = Lg[b-a+1];
Merge(a,c,t);
Merge(b-(1<<t)+1,d-(1<<t)+1,t);
}
LL Ans = 9; // 去掉前导零
vis[findRoot(1,0)] = 1;
for(int i=1; i<=N; ++i){
int now = findRoot(i,0);
if(not vis[now]){
vis[now] = true;
Ans = Ans*10%MOD;
}
}
cout << Ans;
return 0;
}