题目大意:给出一个不定大数,给出一些限制条件,形如:区间l1,r1与l2,r2两个区间中所有的数相等,询问共有多少种合法的大数?
n<=1e5,m<=1e5
30分:
对于给出的区间限定条件,暴力枚举每一个点,利用并查集维护有相等关系的点,最后统计并查集个数,答案就是:9*10^(并查集个数-1) 因为第一个数不为零(他可不是序列。。。
时间复杂度:并查集看作常数,那么总复杂度O(nm),一个暴力。。。
100分:
观察暴力做法,发现维护点之间的对应关系时,暴力枚举的速度实在是太慢,但是我们还是需要知道每一个点的对应关系,所以我们考虑倍增加速。
我们在读入区间时利用二进制拆分将区间分解,再利用并查集合并,通过预处理,利用st表的思想(也不是st表,比较像而已。。。)维护f[i][j],表示从i开始往后2^j这个区间。
那么我们紧接着枚举base(base是2的指数),每一次将区间分一半,再将利用并查集合并,最后还是查询并查集个数(枚举st[i][0](代表每一个点))
时间复杂度:倍增确实是一个好东西,二进制拆分mlogn,区间平分nlogn,总复杂度nlogn(渐近复杂度)
最后:附上本题代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define mod 1000000007 5 #define LL long long 6 using namespace std; 7 8 int n,m; 9 LL fa[10000005],cnt,ans; 10 LL st[1000005][25],start[10000005]; 11 12 int find(int x) 13 { 14 if(fa[x]==x) return x; 15 return fa[x]=find(fa[x]); 16 } 17 void merge(int l1,int l2,int base) 18 { 19 int f1=find(st[l1][base]),f2=find(st[l2][base]); 20 if(f1>f2) swap(f1,f2); 21 fa[f2]=f1; 22 } 23 int main() 24 { 25 scanf("%d%d",&n,&m); 26 for(int j=0;j<=log2(n)+1;j++) 27 { 28 for(int i=1;i<=n;i++) 29 { 30 st[i][j]=++cnt; 31 start[cnt]=i; 32 fa[cnt]=cnt; 33 } 34 } 35 for(int i=1;i<=m;i++) 36 { 37 int l1,r1,l2,r2; 38 scanf("%d%d%d%d",&l1,&r1,&l2,&r2); 39 for(int base=log2(n)+1;base>=0;base--) 40 { 41 if(l1+(1<<base)-1<=r1) 42 { 43 merge(l1,l2,base); 44 l1+=(1<<base); 45 l2+=(1<<base); 46 } 47 } 48 } 49 for(int base=log2(n)+1;base>=1;base--) 50 { 51 for(int i=1;i+(1<<base)-1<=n;i++) 52 { 53 int f=find(st[i][base]),sta=start[f]; 54 merge(i,sta,base-1); 55 merge(i+(1<<(base-1)),sta+(1<<(base-1)),base-1); 56 } 57 } 58 ans=9; 59 for(int i=2;i<=n;i++) 60 { 61 if(fa[st[i][0]]==st[i][0]) 62 { 63 ans*=10; 64 ans%=mod; 65 } 66 } 67 printf("%lld ",ans%mod); 68 return 0; 69 }