SCOI2016 萌萌哒 题解
实在是一个妙题
我们首先考虑两个区间完全相等可以转化为对应点相等,对应的点相等即可以看作他们是一个相同的点。
于是我们有一个暴力:利用并查集,相同的点合并到一起,最后可以得到有多少个并查集。然后,我们可以用简单的计数知识可以知道最后的答案就是\(9*10^{tot-1}\),因为最高位不能为0。
考虑这样做的复杂度的预处理在\(O(N^2)\),而查询答案则是\(O(N)\),复杂度不均衡,我们要考虑一种方法让他的复杂度都均衡为\(O(N\log N)\)。
于是我们可以发现,并查集的操作有可合并性,我们可以利用倍增实现优化,即:开大约20个并查集,表示某一个点往后\(2^i\)的区间和某一区间相同,这样我们可以通过倍增完成预处理,再分裂大的并查集为最小的,最后查询答案,每一步的复杂度都是\(O(N\log N)\),非常OK。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
#define orz cout<<"lyakioi!!!!!!!!!!!!!!!!!"<<endl
inline int r(){int s=0,k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){s=s*10+c-'0';c=getchar();}return s*k;}
int fa[3000001],t[1000001][25],n,m,cnt,mp[3000001];
bool b[3000001];
const int mod=1e9+7;
int father(int x)
{
if(fa[x]!=x)fa[x]=father(fa[x]);
return fa[x];
}
void unit(int x,int y)
{
int fax=father(x);
int fay=father(y);
fa[fax]=fay;
}
int pw(int a,int b)
{
int ans=1;
while(b)
{
if(b&1)ans*=a,ans%=mod;
a*=a;
a%=mod;
b>>=1;
}
return ans;
}
signed main()
{
n=r();m=r();
int now=1,mx=0;
while(1)
{
if(now>n)
{
mx--;
break;
}
now*=2;mx++;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=mx;j++)
{
t[i][j]=(++cnt);
mp[cnt]=i;
}
}
for(int i=1;i<=cnt;i++)fa[i]=i;
int l1,r1,l2,r2;
for(int i=1;i<=m;i++)
{
l1=r();r1=r();l2=r();r2=r();
if(l1>l2)
{
swap(l1,l2);
swap(r1,r2);
}
int k=0,len=r1-l1+1;
while(len)
{
if(len&1)
{
unit(t[l1][k],t[l2][k]);//?
l1+=(1<<k);
l2+=(1<<k);
}
k++;
len>>=1;
}
}
// orz;
for(int i=mx;i;i--)
for(int j=1;j<=n;j++)
{
int x=t[j][i];
int fax=father(x);
if(x==fax)continue;
int y=mp[fax];//连往别人
unit(t[j][i-1],t[y][i-1]);
unit(t[j+(1<<(i-1))][i-1],t[y+(1<<(i-1))][i-1]);
}
// orz;
int tot=0;
for(int i=1;i<=n;i++)
{
int x=father(t[i][0]);
if(!b[x])
{
tot++;
b[x]=1;
}
}
cout<<9*pw(10,tot-1)%mod;
}
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15152210.html