题目描述:
题意:给你一个长度和多个限制,限制指给定的两个区间内的值必须相等,问一共有几种情况。
哪里萌萌哒了
思路分析:我们能想到的最暴力的思路应该就是用并查集了,每给定两个区间,我们就把这两个区间上相应的点都并在一起,来表示它们的值相等,最后统计一下一共有多少个并查集就行了,答案就是9*10^(n-1)(n是并查集数量),因为第一个数不能为0,所以只有九种情况。
但这样是一定会超时的,于是我们来想办法进行优化,看到区间,你想到了什么?ST表?线段树?对了,这道题我们就可以用ST表的思路来进行优化,我们在进行并查集时可以用p[i][j]表示以i为起点的点,向后2^j的长度的区间,这样在合并时就不用再一个点一个点的合并了,就相当于对区间进行了二进制拆分。但是我们这样并不能解决输出的问题,因为此时每个点都被绑在区间上,没有自己的并查集,于是我们就需要将每个点再和它区间的祖先进行合并,这样之后就可以再统计并查集的数目进行计算输出了。其他细节详见注释。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 typedef long long ll; 5 const int N=1e5+10; 6 const int Max=21; 7 const int Mod=1000000007; 8 int fa[N][Max]; 9 int find(int x,int y){//查找p[x][y]的祖先 10 if(fa[x][y]==x) return x; 11 return fa[x][y]=find(fa[x][y],y); 12 } 13 void merge(int x,int y,int i){//合并 14 x=find(x,i);y=find(y,i); 15 if(x!=y){ 16 fa[x][i]=y; 17 } 18 } 19 int main(){ 20 int n,m; 21 scanf("%d%d",&n,&m); 22 for(int i=1;i<=n;++i) //并查集初始化 23 for(int j=0;j<Max;++j) 24 fa[i][j]=i; 25 for(int i=1;i<=m;++i){ 26 int l1,l2,r1,r2; 27 scanf("%d%d%d%d",&l1,&r1,&l2,&r2); 28 for(int j=Max-1;~j;--j){//对拆出来的每一段进行合并 29 if(l1+(1<<j)-1<=r1){ 30 merge(l1,l2,j); 31 l1+=(1<<j);l2+=(1<<j);//走下一段 32 } 33 } 34 } 35 for(int j=Max-1;j;--j){ //将祖先分到每个区间的节点上 36 for(int i=1;i+(1<<j)-1<=n;++i){ 37 merge(i,find(i,j),j-1); 38 merge(i+(1<<j-1),find(i,j)+(1<<j-1),j-1); 39 } 40 } 41 int num=0; 42 ll ans=9; 43 for(int i=1;i<=n;++i) //计算答案 44 if(find(i,0)==i) num++; 45 for(int i=1;i<num;++i){ 46 ans=(ans*10)%Mod; 47 } 48 printf("%lld ",ans); 49 return 0; 50 }