[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 }

 

posted @ 2018-04-28 22:07  Z-Y-Y-S  阅读(368)  评论(0编辑  收藏  举报