【SCOI 2016】萌萌哒

【题目】

题目传送门萌萌哒

题目描述

一个长度为 n 的大数,用 S_{1}S_{2}S_{3}S_{n} 表示,其中 S_{i} 表示数的第 i 位, S_{1} 是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l_{1}r_{1}l_{2}r_{2} ,即两个长度相同的区间,表示子串S_{l1}S_{l1+1}S_{l1+2}...S_{r1} 与 S_{l2}S_{l2+1}S_{l2+2}...S_{r2} 完全相同。

比如 n=6 时,某限制条件 l_{1}=1,r_{1}=3,l_{2}=4,r_{2}=6,那么 123123,351351 均满足条件,但是 12012,131141 不满足条件,前者数的长度不为 6,后者第二位与第五位不同。

问满足以上所有条件的数有多少个。

输入格式

第一行两个数 n 和 m ,分别表示大数的长度,以及限制条件的个数。
接下来 m 行,对于第 i 行, 有 4 个数 l_{i1} ,r_{i1} ,l_{i2} ,r_{i2} ,分别表示该限制条件对应的两个区间。

输出格式

一个数,表示满足所有条件且长度为 n 的大数的个数,答案可能很大,因此输出答案模 10^{9}+7 的结果即可。

样例数据

输入
4 2
1 2 3 4
3 3 3 3

输出
90

备注

【数据范围】
对于 30% 的数据,1 ≤ n ≤ 2000,1 ≤ m ≤ 2000;
对于 100% 的数据,1 ≤ n ≤ 10^{5},1 ≤ m ≤ 10^{5},1 ≤ l_{i1} , r_{i1} , l_{i2} , r_{i2} ≤ n;
并且保证 r_{i1} - l_{i1} = r_{i2} - l_{i2} 。

【分析】

所以说这道题跟萌萌哒有什么关系吗

不过这道题经dzy大佬讲解之后依旧不是很懂,果然我还是太蒟蒻了吗

30分做法:我们将每次限制的 l_{1} 和 l_{2} 放入同一个集合,l_{1}+1 和 l_{2}+1 放入同一个集合……r_{1} 和 r_{2} 放入同一个集合,在同一个集合中的元素代表在这些位置上我们必须放相同的数,这样的话我们可以用并查集,假设最后有 num 个集合,为了保证这个数有 n 位,第一位就不能为 0,那么最后的答案是 9\times 10^{num-1} ,时间复杂度为O(nm)

100分做法:这个做法实际上是30分基础上的一个优化,上面的做法对 l_{1}~r_{1}l_{2}~r_{2})中每一位都合并了一次,是O(n)的复杂度,我们就从这个角度优化,每次合并的时候以区间来合并,而不是按照每一位来合并,这个怎么实现呢,用倍增,我们用 father[ i ][ j ] 表示以 i 为起点往后 2^j 个位置这个区间的父亲(父亲就是这个集合中某一区间的左端点),这样的话复杂度就降到了O(log n),然后我们把每个区间向下传递到更小的区间直到又传到某一位,再统计答案就行了

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define L 25
#define N 100005
#define mod 1000000007
using namespace std;
int father[N][L];
int find(int x,int y)
{
	if(father[x][y]==x)  return x;
	return father[x][y]=find(father[x][y],y);
}
void merge(int x,int y,int i)
{
	x=find(x,i);
	y=find(y,i);
	if(x!=y)
	  father[x][i]=y;
}
int main()
{
	int n,m,i,j;
	int l1,r1,l2,r2;
	int num=0,ans=9;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	  for(j=0;j<=L;++j)
	    father[i][j]=i;
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&l1,&r1);
		scanf("%d%d",&l2,&r2);
		for(j=L;j>=0;--j)
		{
			if(l1+(1<<j)-1<=r1)
			{
				merge(l1,l2,j);
				l1+=(1<<j);
				l2+=(1<<j);
			}
		}
	}
	for(j=L;j;--j)
	{
		for(i=1;i+(1<<j)-1<=n;++i)
		{
			merge(i,find(i,j),j-1);
			merge(i+(1<<j-1),find(i,j)+(1<<j-1),j-1);
		}
	}
	for(i=1;i<=n;++i)
	  if(find(i,0)==i)
	    num++;
	for(i=2;i<=num;++i)
	  ans=(10ll*ans)%mod;
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/81453409
今日推荐