- 【题目地址】
题目大意看原题面。
我们首先考虑,对于每个相等的数,我们用并查集将其并起来,那么由于不能有前导0,令最后集合的个数为 c c c,所以答案就是 9 × 1 0 c − 1 9\times 10^{c-1} 9×10c−1,除了开头不能选0,只有9中选择方案外,其余的每个数字都有10种选择方案(只有一位数字的时候需要特判,因为此时0也算)。
由于暴力的合并的话,最坏的复杂度会达到 O ( n m l o g n ) O(nmlogn) O(nmlogn),就只有30分,那么由于每次都并的一整块,所以我们用倍增的方式,将一块一块二进制拆分后并起来,那么每次合并只需 O ( l o g 2 n ) O(log^2n) O(log2n)的时间,然后由于最后我们需要知道每一个元素属于哪个集合,所以在从大到小的将块拆开,推下去,最后就可以得知每个元素的所属集合,复杂度 O ( n l o g n ) O(nlogn) O(nlogn),所以最后看一下有多少个集合即可,最终复杂度为 O ( m l o g 2 n + n l o g n ) O(mlog^2n+nlogn) O(mlog2n+nlogn)
大的块拆分的方式如下:
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7,Log=22;
const int M=1e5+10;
int n,m;
int f[M][Log];
int find(int a,int dis){return f[a][dis]==a?a:f[a][dis]=find(f[a][dis],dis);}
void init(){for(int i=0;i<Log;i++)for(int j=1;j<=n;j++)f[j][i]=j;}
void merge(int a,int b,int dis){
int f1=find(a,dis),f2=find(b,dis);
if(f1!=f2)f[f[a][dis]][dis]=f[b][dis];
}
ll ans=9ll;
ll fpow(ll a,ll b){
ll res=1;
for(;b;b>>=1,a=(a*a)%mod)
if(b&1)res=(res*a)%mod;
return res;
}
int l1,l2,r1,r2;
void file(){
freopen("moe.in","r",stdin);
freopen("moe.out","w",stdout);
}
void close(){
fclose(stdin);
fclose(stdout);
}
int cnt,now;
int main(){
file();
scanf("%d%d",&n,&m);
if(n==1){puts("10");return 0;}
init();
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
for(int j=Log-1;j>=0;j--){
if(l1+(1<<j)-1<=r1)merge(l1,l2,j),l1+=(1<<j),l2+=(1<<j);
}
}
for(int j=Log-1;j;j--){
for(int i=1;i+(1<<j)-1<=n;i++){
merge(i,find(i,j),j-1);
merge(i+(1<<(j-1)),f[i][j]+(1<<(j-1)),j-1);
}
}
for(int i=1;i<=n;i++)if(find(i,0)==i) cnt++;
ans=ans*fpow(10,cnt-1)%mod;
printf("%lld\n",ans);
close();
return 0;
}