[SCOI2016]萌萌哒
题目描述
一个长度为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,后者第二位与第五位不同。问满足以上所有条件的数有多少个。
输入输出格式
输入格式:第一行两个数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。
输出格式:一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模10^9+7的结果即可。
输入输出样例
输入样例#1:
复制
4 2 1 2 3 4 3 3 3 3
输出样例#1: 复制
90
用并查集维护连通关系
设logn个并查集
第i个并查集的j点与k点连通表示[j~j+2^i]=[k~k+2^i]
于是就可以从后往前,把第i个并查集的信息传到第i-1个
设第0个并查集有cnt个连通块
那么答案就是$9*10^{cnt-1}$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int set[25][100011],ans,Mod=1e9+7,n,m,Log[100011],cnt; 8 int find(int t,int x) 9 { 10 if (set[t][x]!=x) set[t][x]=find(t,set[t][x]); 11 return set[t][x]; 12 } 13 void merge(int t,int x,int y) 14 { 15 int p=find(t,x); 16 int q=find(t,y); 17 if (p!=q) 18 { 19 set[t][p]=q; 20 } 21 } 22 int main() 23 {int i,j,l1,l2,r1,r2,len; 24 cin>>n>>m; 25 for (i=2;i<=n;i++) 26 { 27 Log[i]=Log[i/2]+1; 28 } 29 for (i=0;i<=Log[n];i++) 30 for (j=1;j<=n;j++) 31 set[i][j]=j; 32 for (i=1;i<=m;i++) 33 { 34 scanf("%d%d%d%d",&l1,&r1,&l2,&r2); 35 len=Log[r1-l1+1]; 36 merge(len,l1,l2); 37 merge(len,r1-(1<<len)+1,r2-(1<<len)+1); 38 } 39 for (i=Log[n];i>=1;i--) 40 { 41 for (j=1;j+(1<<i)-1<=n;j++) 42 { 43 int k=find(i,j); 44 if (j==k) continue; 45 merge(i-1,j,k); 46 merge(i-1,j+(1<<i-1),k+(1<<i-1)); 47 } 48 } 49 ans=9; 50 for (i=1;i<=n;i++) 51 if (find(0,i)==i) cnt++; 52 for (i=1;i<cnt;i++) 53 ans=1ll*10*ans%Mod; 54 cout<<ans; 55 }