【SCOI2016】萌萌哒
Cry_For_theMoon · · 题解
传送门
尝试写一篇能让初学倍增的新人可以看懂的题解QwQ
显然从优化合并区间开始考虑
并查集的合并是满足结合律的,就是说如果区间
倍增显然是针对
问题变成了若干个长度为
最后我们求的显然是
根据一开始的结合律,两个区间合并(合并完自然连通,连通一定是因为合并,两者可以视作等价),这两个区间祖先相同可以变成它们拆分后得到的四个子区间祖先相同。那么合并四个子区间就可以了,既然这里区间长度是
最后传递到第一层(
//SCOI,2016
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=1e5+10,MODER=1e9+7;
int n,m,fa[MAXN][30],power[30],vis[MAXN];
int l1,l2,r1,r2,cnt;
long long ans;
int find(int x,int k){
if(fa[x][k] == x)return x;
return fa[x][k] = find(fa[x][k],k);
}
long long getpow(int n){
if(n==0)return 1;
if(n==1)return 10;
long long tmp = getpow(n/2);
tmp = (tmp*tmp) % MODER;
if(n&1){
tmp = (tmp*10)%MODER;
}
return tmp;
}
int main(){
scanf("%d%d",&n,&m);
power[0] = 1;
for(int i=1;i<=20;i++){
power[i] = power[i-1]<<1;
}
for(int idx=0;power[idx]<=n;idx++){
for(int i=1;i+power[idx]-1<=n;i++){
fa[i][idx] = i; //这里并查集按层,每一层祖先互不相同即可
}
}
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
int idx = log(r1-l1+1)/log(2);
int fl1 = find(l1,idx);
int fl2 = find(l2,idx);
fa[fl1][idx] = fl2;
int fr1 = find(r1-power[idx]+1,idx);
int fr2 = find(r2-power[idx]+1,idx);
fa[fr1][idx] = fr2;
}
for(int idx=log(n)/log(2);idx>=1;idx--){
//拆分每个区间
for(int i=1;i+power[idx]-1<=n;i++){
int f = find(i,idx);
if(f==i)continue;
int fa1 = find(i,idx-1);
int fa2 = find(f,idx-1);
fa[fa1][idx-1] = fa2;
fa1 = find(i+power[idx-1],idx-1);
fa2 = find(f+power[idx-1],idx-1);
fa[fa1][idx-1] = fa2;
}
}
for(int i=1;i<=n;i++){
int f = find(i,0);
if(!vis[f]){
cnt++;
vis[f] = 1;
}
}
ans = (9 * getpow(cnt-1) )%MODER;
printf("%lld\n",ans);
return 0;
}