4569: [Scoi2016]萌萌哒
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 459 Solved: 209
[Submit][Status][Discuss]
Description
一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...Sr2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,131141不满足条件,前者数的长度不为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
1 2 3 4
3 3 3 3
Sample Output
90
HINT
Source
Solution
这道题非常巧妙
先思考暴力,对每一位维护一个并查集,每次限制就是把那两个区间信息合并,最后答案根据剩有的计算即可
那么问题在于如何快速的合并
考虑线段树,分出来的区间过多,合并还是有问题,所以换种方法
倍增!建立ST表,相当于对同层的建出一棵类似树的东西,每层维护并查集,相当于把信息拆成$2^{?}$的两段
合并优先合并大的,这样一共是$nlogn$段,最多合并$nlogn$次
递归合并,发现合并过就可以跳出了,总的答案统计一下用快速幂计算一下即可
特判N=1
总的时间复杂度$O(nlogn*a(n))$
启发:倍增并不仅仅应用于LCA或SA求LCP之类的,应该灵活运用这种思想
Code
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> using namespace std; void Freopen() {freopen("game.in","r",stdin); freopen("game.out","w",stdout);} #define P 1000000007 inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } int N,M; long long Quick_Pow(long long x,long long y) { long long re=1; for (int i=y; i; i>>=1,x=x*x%P) if (i&1) re=re*x%P; return re; } #define MAXN 100010 int fa[MAXN][21]; int Find(int x,int y) {if (fa[x][y]==x) return x; else return fa[x][y]=Find(fa[x][y],y);} void Merge(int x1,int x2,int k) { int f1=Find(x1,k),f2=Find(x2,k); if (f1==f2) return; fa[f1][k]=f2; if (k--) {Merge(x1,x2,k),Merge(x1+(1<<k),x2+(1<<k),k);} } int Len; int main() { Freopen(); N=read(),M=read(); for (int i=1; i<=N; i++) for (int j=0; j<=17; j++) fa[i][j]=i; while (M--) { int l1=read(),r1=read(),l2=read(),r2=read(); int k=0; while ((1<<k)<=r1-l1+1) k++; k--; Merge(l1,l2,k); Merge(r1-(1<<k)+1,r2-(1<<k)+1,k); } for (int i=1; i<=N; i++) if (fa[i][0]==i) Len++; printf("%d ",N==1? 10:int(9LL*Quick_Pow(10,Len-1)%P)); return 0; }