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; }