4569: [Scoi2016]萌萌哒
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1262 Solved: 610
[Submit][Status][Discuss]
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
1 2 3 4
3 3 3 3
Sample Output
90
HINT
Source
考虑朴素算法,对每个询问的每一位进行合并,复杂度O(N*N)
我们发现其中有许多重复限制,我们要考虑减少重复计算本质上相同的限制,于是我们用倍增优化
f[i][j]表示[i,i+2^j-1]这一区间的情况
对于每一个限制,可以拆成两个大的限制,然后逐层向下传递,直到某一层的两个区间处于同一集合,也就说明这次限制之前已经又过了,就不需要再向下处理。
复杂度O(NlgN)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<cmath> 6 #include<algorithm> 7 #define maxn 100005 8 using namespace std; 9 int fa[maxn][20]; 10 int find(int x,int t) {return fa[x][t]==x?fa[x][t]:fa[x][t]=find(fa[x][t],t);} 11 void merge(int x,int y,int t) { 12 int fx=find(x,t),fy=find(y,t); 13 if(fx==fy) return; 14 fa[fx][t]=fy; 15 if(!t) return; 16 merge(x,y,t-1);merge(x+(1<<(t-1)),y+(1<<(t-1)),t-1); 17 } 18 int n,m; 19 int lg[maxn]; 20 bool vis[maxn]; 21 int main() { 22 scanf("%d%d",&n,&m); 23 for(int i=1;(1<<i)<=n;i++) lg[1<<i]++; 24 for(int i=1;i<=n;i++) lg[i]+=lg[i-1]; 25 for(int i=1;i<=n;i++) for(int j=0;j<20;j++) fa[i][j]=i; 26 for(int i=1;i<=m;i++) { 27 int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2); 28 int len=r1-l1+1; 29 merge(l1,l2,lg[len]);merge(r1-(1<<lg[len])+1,r2-(1<<lg[len])+1,lg[len]); 30 } 31 long long ans=9;vis[find(1,0)]=1; 32 for(int i=2;i<=n;i++) { 33 if(!vis[find(i,0)]) ans*=10ll,ans%=1000000007; 34 vis[find(i,0)]=1; 35 } 36 printf("%lld\n",ans); 37 }