BZOJ4569: [Scoi2016]萌萌哒
Description
Input
Output
一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模10^9+7的结果即可。
Sample Input
1 2 3 4
3 3 3 3
Sample Output
题解Here!
首先我们发现题目中给了我们一些区间相等的关系。
之后我们发现,如果从子串的思路想会进入死胡同,因为串都没给你哪里来的后缀数据结构?
发现这道题本质上是个计数问题,我们考虑枚举每个点可以取的值,发现有些其他点必须和他取值一样,否则不满足题意区间相等的要求。
这里其实做了一步转化,区间相等$\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;
}

浙公网安备 33010602011771号