Link
一个很trivial的想法是并查集暴力维护,最后的答案是\(9*10^n\),\(n\)是最后剩下的并查集的个数。
因为每次连边的都是一段区间到另一端区间,所以我们可以二进制拆分,把并查集的每个点拆成\(\log\)个点,每次把要连边的区间拆成\(\log\)个点直接连,最后再还原。
#include<cstdio>
#include<cctype>
#include<numeric>
#include<algorithm>
using std::iota;
const int N=100007,P=1000000007;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int mod(int a){return a+(a>>31&P);}
int inc(int a,int b){return mod(a+b-P);}
int dec(int a,int b){return mod(a-b);}
int mul(int a,int b){return 1ll*a*b%P;}
int power(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int n,m,cnt,id[20][N],Log[N],vis[N],fa[20*N];
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
void merge(int u,int v){fa[find(u)]=find(v);}
void get(int x,int&y){x%=n,y=x?x:n;}
int main()
{
n=read(),m=read();int ans=0;
for(int i=2;i<=n;++i) Log[i]=Log[i>>1]+1;
for(int i=0;i<=Log[n];++i) for(int j=1;j<=n;++j) id[i][j]=++cnt;
iota(fa+1,fa+cnt+1,1);
for(int i=1;i<=m;++i)
{
int l=read(),r=read(),len=read(),d=read();
d=len-l,len=r-l+1;
for(int k=0;len;len>>=1,++k) if(len&1) merge(id[k][l+((len>>1)<<(k+1))],id[k][l+((len>>1)<<(k+1))+d]);
}
for(int i=Log[n];i;--i)
for(int j=1,f,x;j+(1<<i)-1<=n;++j)
{
if((f=find(id[i][j]))==id[i][j])continue;
get(f,x),merge(id[i-1][j],id[i-1][x]),merge(id[i-1][j+(1<<(i-1))],id[i-1][x+(1<<(i-1))]);
}
for(int i=1,x;i<=n;++i)
{
get(find(id[0][i]),x);
if(!vis[x]) vis[x]=1,++ans;
}
printf("%d",mul(9,power(10,ans-1)));
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12207753.html
时间: 2024-10-29 07:16:12