题目描述
一个长度为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:
View Code
90
正解:
首先想到对于相同的数可以放到一个集合里,用并查集维护
有多少个集合(假设有cnt个集合)
那么最后答案就是ans=9*(10^(cnt-1))%mod
证明:第一个位置数字可以为9种(不能为0)
第二个位置数字可以为10种(0-9)
第三个位置数字可以为10种(0-9)
.........
一共有cnt个位置,即ans;
但是暴力的话,很明显会超时,怎们办呢?
我们可以用st表维护。
某个区间看成一个节点,他的两个儿子拼起来即为自身区间
(按照倍增的区间分法)
当进行合并的时候,我们对区间进行合并而不是点。
最后,我们按照区间长度从大到小依次考察每个区间,
如果他并不是一个独立的区间,那么我们就把他的限制
放给他的儿子,即合并操作,直到区间长度为1
到最后统计一下集合的个数就可以得出答案

1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<string> 6 #include<cmath> 7 #define ll long long 8 #define DB double 9 #define inf 214748360000 10 #define mod 1000000007 11 using namespace std; 12 inline int read() 13 { 14 int x=0,w=1;char ch=getchar(); 15 while(!isdigit(ch)){if(ch==‘-‘) w=-1;ch=getchar();} 16 while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); 17 return x*w; 18 } 19 const int N=1e5+90; 20 int n,fa[N*18],m,cnt,ch[N*18][2]; 21 int f[N][18]; 22 int F(int x) 23 { 24 if(x==fa[x]) return x; 25 else return fa[x]=F(fa[x]); 26 } 27 void merge(int x,int y) 28 { 29 int fx=F(x),fy=F(y); 30 fa[fx]=fa[fy]=fx; 31 } 32 int main() 33 { 34 n=read();m=read(); 35 for(int j=0;(1<<j)<=n;++j) 36 for(int i=1;i+(1<<j)-1<=n;++i) 37 { 38 f[i][j]=++cnt;fa[cnt]=cnt; 39 if(j) 40 { 41 ch[cnt][0]=f[i][j-1]; 42 ch[cnt][1]=f[i+(1<<(j-1))][j-1]; 43 } 44 } 45 while(m--) 46 { 47 int l1,r1,l2,r2; 48 l1=read();r1=read();l2=read();r2=read(); 49 int j=log2(r2-l2+1); 50 merge(f[l1][j],f[l2][j]); 51 merge(f[r1-(1<<j)+1][j],f[r2-(1<<j)+1][j]); 52 } 53 for(int i=cnt;i>n;--i) 54 { 55 int t=F(i); 56 if(t!=i) 57 { 58 merge(ch[i][0],ch[t][0]); 59 merge(ch[i][1],ch[t][1]); 60 } 61 } 62 cnt=0; 63 for(int i=1;i<=n;++i) 64 cnt+=(F(i)==i); 65 int ans=9; 66 for(int i=1;i<=cnt-1;++i) 67 ans=1ll*ans*10%mod; 68 printf("%d",ans); 69 return 0; 70 }
走到悬崖处,想办法,就有路了(搭个梯子??)。