BZOJ4569: [Scoi2016]萌萌哒

BZOJ4569: [Scoi2016]萌萌哒

Description

一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条
件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...S
r2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,13
1141不满足条件,前者数的长度不为6,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

Input

第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。接下来m行,对于第i行,有4个数li1,ri1,li2
,ri2,分别表示该限制条件对应的两个区间。
1≤n≤10^5,1≤m≤10^5,1≤li1,ri1,li2,ri2≤n;并且保证ri1-li1=ri2-li2。

Output

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

Sample Input

4 2
1 2 3 4
3 3 3 3

Sample Output

90

题解Here!
这题目想了半天没思路。。。
感觉后缀数组并不可做,$SAM$又没学过。。。
然后看了发题解,神奇的倍增。。。
真是被高级数据结构洗脑了。。。
其实倍增法不仅局限于快速幂,后缀数组,ST表,求lca之类的特定算法,而是用来解决一类特定问题的。

首先我们发现题目中给了我们一些区间相等的关系。

之后我们发现,如果从子串的思路想会进入死胡同,因为串都没给你哪里来的后缀数据结构?

发现这道题本质上是个计数问题,我们考虑枚举每个点可以取的值,发现有些其他点必须和他取值一样,否则不满足题意区间相等的要求。

这里其实做了一步转化,区间相等$\Leftrightarrow$对应点相等。

那么我们就有办法维护这个相等关系了。

直接并查集维护,复杂度$O(n^2)$,$30$分,最后的答案为$9*10^{num-1}$ $num$为并查集的根的个数,因为最高位是不可以取零的。

然后发现是传统暴力的复杂度不均衡问题,我们的处理询问,是$O(N^2)$的。

但是,我们的查找,仅仅是$O(N)$的,这就比较尴尬了。。。

所以我们要平衡这个扭曲的复杂度,使得询问查找都变成$O(N\log_2N)$。

别问我为什么不是$O(N\sqrt N)$。。。

那么怎么办呢,并查集的并操作具有可合并性,所以我们可以考虑上倍增。

我们可以将一个点拆成$\log_2N$个点,分别代表从点$i$开始,长度为$2^k$的子串。

那么当我们处理两个区间相等的关系时,对区间做二进制拆分,拆成$log$个区间,分别并起来即可。

当然我们这样做修改是省心了,但是同时查询的时候也会带来一些麻烦。

因为,我们要求的信息是最底层的,只能是长度为$1$的区间,而不能有奇奇怪怪的区间。

不过没关系,我们这时运用等式$1$,拆分并查集。

具体来讲,我们从最长的区间开始逐个枚举,每次查找他和他的父亲,然后把它和父亲都劈成两半,前一半和前一半连边,后一半和后一半连边即可,这样相当于把较长区间并查集拆成两个一半的并查集。

最后我们就有了一些关于那些点相等的信息,直接计算并查集个数即可。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 100010
#define MOD 1000000007LL
using namespace std;
int n,m;
long long ans=9;
int f[MAXN][20];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
long long mexp(long long a,long long b,long long c){
	long long s=1;
	while(b){
		if(b&1)s=s*a%c;
		a=a*a%c;
		b>>=1;
	}
	return s;
}
int find(int x,int y){return f[x][y]==x?x:f[x][y]=find(f[x][y],y);}
void uniun(int x,int y,int len){if(find(x,len)!=find(y,len))f[f[x][len]][len]=f[y][len];}
void step(){
	for(int i=19;i>=1;i--)
	for(int j=1;j+(1<<i)-1<=n;j++){
		uniun(j,find(j,i),i-1);
		uniun(j+(1<<(i-1)),f[j][i]+(1<<(i-1)),i-1);
	}
}
void work(){
	int k=0;
	for(int i=1;i<=n;i++)if(find(i,0)==i)k++;
	ans=ans*mexp(10,k-1,MOD)%MOD;
	printf("%lld\n",ans);
}
void init(){
	int l1,l2,r1,r2;
	n=read();m=read();
	for(int i=0;i<=19;i++)
	for(int j=1;j<=n;j++)
	f[j][i]=j;
	for(int i=1;i<=m;i++){
		l1=read();r1=read();l2=read();r2=read();
		for(int i=19;i>=0;i--)if(l1+(1<<i)-1<=r1){
			uniun(l1,l2,i);
			l1+=(1<<i);l2+=(1<<i);
		}
	}
	step();
}
int main(){
	init();
	work();
    return 0;
}

 

posted @ 2018-08-24 23:51  符拉迪沃斯托克  阅读(242)  评论(0编辑  收藏  举报
Live2D