题目链接
解法:倍增+并查集
先想暴力的做法。每次读入
l
1
,
r
1
,
l
2
,
r
2
l_1,r_1,l_2,r_2
l1,r1,l2,r2,令
i
i
i从
0
0
0枚举到
r
1
−
l
1
r_1-l_1
r1−l1,然后用并查集合并
l
1
+
i
l_1+i
l1+i和
l
2
+
i
l_2+i
l2+i,最后统计并查集中联通块的个数
s
s
s,答案即为
9
×
1
0
s
−
1
9×10^{s-1}
9×10s−1。
但是这样会TLE。
对暴力进行优化。更改并查集中数组
f
f
f的含义,令
f
x
,
k
f_{x,k}
fx,k表示从
x
x
x到
x
+
2
k
−
1
x+2^k-1
x+2k−1这两段的共同编号,亦即若
f
x
,
k
=
f
y
,
k
f_{x,k}=f_{y,k}
fx,k=fy,k则表示
f
x
,
…
,
f
x
+
2
k
−
1
f_x,\dots,f_{x+2^k-1}
fx,…,fx+2k−1和
f
y
,
…
,
f
y
+
2
k
−
1
f_y,\dots,f_{y+2^k-1}
fy,…,fy+2k−1这两段数每项分别处于相同的联通块中。合并时用倍增的处理方式(参考ST表)。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const long long p=1000000007ll;
long long ans=1ll;
int n,m,l1,l2,r1,r2,f[17][100001];
bool flag=false;
int find(int x,int k){
return f[k][x]==x?x:f[k][x]=find(f[k][x],k);
}
void merge(int x,int y,int k){
if(y>100000)return;
int fx=find(x,k),fy=find(y,k);
if(fx!=fy)f[k][fx]=fy;
}
void update(int a,int b,int c,int d){
int k=(int)log2(b-a+1);
merge(a,c,k),merge(b-(1<<k)+1,d-(1<<k)+1,k);
}
int main(){
scanf("%d%d",&n,&m);int l=(int)log2(n);
for(int i=1;i<=n;++i)for(int k=0;k<17;++k)f[k][i]=i;
for(int i=1;i<=m;++i)scanf("%d%d%d%d",&l1,&r1,&l2,&r2),update(l1,r1,l2,r2);
for(int i=l;i;--i)for(int j=1;j<=n-(1<<i)+1;++j){int f=find(j,i);merge(j,f,i-1),merge(j+(1<<i-1),f+(1<<i-1),i-1);}
for(int i=1;i<=n;++i)if(find(i,0)==i)(ans*=(flag?10:((flag=true),9)))%=p;
printf("%lld",ans);
}