【BZOJ 4569】 4569: [Scoi2016]萌萌哒 (倍增+并查集)
4569: [Scoi2016]萌萌哒
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 865 Solved: 414Description
一个长度为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,后者第二位与第五位不同。问满足以上所有条件的数有多少个。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 3Sample Output
90HINT
Source
【分析】
这题的方法还是很妙的。虽然说不是怎么新奇但是我还是没想出来嘛。。
其实个人觉得线段树也是可以的。用线段树上的点表示区间,然后也可以merge。
倍增也很好打啦。
f[i][j]表示[i,i+2^j-1]这个区间,同层的区间可以merge。
m个操作的时候就merge最多log个区间嘛。最后把merge的并查集下放到区间长度小一倍那里就好。
要细心一点,若全都在一个并查集,答案应该是10而不是9。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Mod 1000000007 8 #define Maxn 100010 9 #define Maxd 20 10 11 int f[Maxn][Maxd],num[Maxn][Maxd],fa[Maxn*Maxd]; 12 int son[Maxn*Maxd][2]; 13 14 int ffa(int x) 15 { 16 if(fa[x]!=x) fa[x]=ffa(fa[x]); 17 return fa[x]; 18 } 19 20 void merge(int st1,int st2,int l) 21 { 22 for(int j=18;j>=0;j--) 23 { 24 if((1<<j)<=l) 25 { 26 int x=num[st1][j],y=num[st2][j]; 27 if(ffa(x)!=ffa(y)) fa[ffa(x)]=y; 28 if((1<<j)<l) merge(st1+(1<<j),st2+(1<<j),l-(1<<j)); 29 return; 30 } 31 } 32 } 33 34 int main() 35 { 36 int n,m; 37 scanf("%d%d",&n,&m); 38 int cnt=0; 39 for(int j=0;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) 40 { 41 num[i][j]=++cnt; 42 if(j!=0) son[cnt][0]=num[i][j-1],son[cnt][1]=num[i+(1<<j-1)][j-1]; 43 } 44 for(int i=1;i<=cnt;i++) fa[i]=i; 45 for(int i=1;i<=m;i++) 46 { 47 int l1,r1,l2,r2; 48 scanf("%d%d%d%d",&l1,&r1,&l2,&r2); 49 merge(l1,l2,r1-l1+1); 50 } 51 for(int j=18;j>0;j--) 52 { 53 for(int i=1;i<=n;i++) if(num[i][j]) 54 { 55 if(ffa(num[i][j])!=num[i][j]) 56 { 57 int x=num[i][j],y=ffa(num[i][j]); 58 if(ffa(son[x][0])!=ffa(son[y][0])) fa[ffa(son[x][0])]=son[y][0]; 59 if(ffa(son[x][1])!=ffa(son[y][1])) fa[ffa(son[x][1])]=son[y][1]; 60 } 61 } 62 } 63 int ans=1,tot=0; 64 for(int i=1;i<=n;i++) if(ffa(num[i][0])==num[i][0]) 65 { 66 tot++; 67 if(ans==1) ans=1LL*ans*9%Mod; 68 else ans=1LL*ans*10%Mod; 69 } 70 if(tot==1) ans=10; 71 printf("%d\n",ans); 72 return 0; 73 }
2017-04-27 15:02:37