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;
}
posted @ 2021-08-17 14:33  lei_yu  阅读(36)  评论(0编辑  收藏  举报