传送门:bzoj4569
题解
太妙了!
并查集处理,合并复杂度
O(n2)
O
(
n
2
)
,询问复杂度
O(n)
O
(
n
)
。这里用到倍增的技巧优化并查集合并,延迟下传标记。
把每个点拆成
logn
log
n
个点,
f[i][j]
f
[
i
]
[
j
]
分别代表
[j,j+2i−1]
[
j
,
j
+
2
i
−
1
]
区间。对于区间合并,虽然是单个的合并,但是可以先将分到每个二进制段区间上合并代表该区间的点。最后统一处理下传,对于某一层的合并,分别让两个左子区间,两个右子区间合并(左右子区间即区间长度减半的区间)。
复杂度降到
O(nlogn)
O
(
n
log
n
)
代码
#include<bits/stdc++.h>
#define RI register
#define gc getchar()
#define si isdigit(c)
using namespace std;
const int N=1e5+10,mod=1e9+7;
int ans,n,m,bin[25],f[17][N];
char c;
inline void rd(int &x)
{
c=gc;x=0;
for(;!si;c=gc);
for(;si;c=gc) x=x*10+(c^48);
}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=mul(x,x))
if(y&1) re=mul(re,x);
return re;
}
inline int getfa(int jd,int x){return f[jd][x]==x?x:f[jd][x]=getfa(jd,f[jd][x]);}
inline void merge(int jd,int x,int y)
{int ix=getfa(jd,x),iy=getfa(jd,y);if(ix!=iy) f[jd][ix]=iy;}
int main(){
RI int i,j,k,la,ra,lb,rb,len;
rd(n);rd(m);
bin[0]=1;for(i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
for(i=0;i<=16;++i)
for(j=1;j<=n;++j)
f[i][j]=j;
for(;m;--m){
rd(la);rd(ra);rd(lb);rd(rb);
len=ra-la+1;
for(i=0;bin[i]<=len;++i)
if(len&bin[i]){
merge(i,la,lb);
la+=bin[i];lb+=bin[i];
}
}
for(i=16;i>=1;--i){
for(j=1;j+bin[i]-1<=n;++j){
k=getfa(i,j);if(k==j) continue;
merge(i-1,j,k);merge(i-1,j+bin[i-1],k+bin[i-1]);
}
}
for(i=1;i<=n;++i) if(getfa(0,i)==i) ans++;
printf("%d\n",mul(9,fp(10,ans-1)));
}