其他分享
首页 > 其他分享> > P3295 [SCOI2016]萌萌哒——倍增并查集

P3295 [SCOI2016]萌萌哒——倍增并查集

作者:互联网

P3295 [SCOI2016]萌萌哒

题目描述

一个长度为 nn 的大数,用 S_1S_2S_3 \cdots S_nS1​S2​S3​⋯Sn​表示,其中 S_iSi​ 表示数的第 ii 位, S_1S1​ 是数的最高位。告诉你一些限制条件,每个条件表示为四个数,l_1,r_1,l_2,r_2l1​,r1​,l2​,r2​,即两个长度相同的区间,表示子串S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}Sl1​​Sl1​+1​Sl1​+2​⋯Sr1​​与S_{l_2}S_{l_2+1}S_{l_2+2} \cdots S_{r_2}Sl2​​Sl2​+1​Sl2​+2​⋯Sr2​​完全相同。

比如 n=6n=6 时,某限制条件 l_1=1,r_1=3,l_2=4,r_2=6l1​=1,r1​=3,l2​=4,r2​=6 ,那么 123123123123,351351351351 均满足条件,但是 1201212012,131141131141 不满足条件,前者数的长度不为 66 ,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

输入格式

第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。

接下来m行,对于第i行,有4个数 li1,ri1,li2,ri2,分别表示该限制条件对应的两个区间。

1\le n\le 10^51≤n≤105,1\le m\le 10^51≤m≤105 ,1\le li1,ri1,li2,ri2 \le n1≤li1,ri1,li2,ri2≤n ;并且保证 ri1-li1=ri2-li2ri1−li1=ri2−li2 。

输出格式

一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模 10^9+7109+7 的结果即可。

输入输出样例

输入 #1
4 2
1 2 3 4
3 3 3 3
输出 #1
90


自己写的时候脑抽了。。。。竟然以为是一个后缀数组的逆序问题。。。。。

我们想,对于一个位置,和他连接的必然值和他相同,这让我们想到了并查集。

那最后乘法原理得出的解就是9*10^(连通块的个数-1)(最高位为0的情况排除!)

然而数据范围给力。考虑优化。数据范围得知大致为O(nlogn),嗯想到辣倍增

处理f[i][j]为i---i+2^j-1的所属连通块,在每次限制的时候,倍增修改一下。

最后把大的推广到小的,即f[i][j]->f[i][j-1]和f[i+(1<<j)][j-1],

再查询一下连通块数量都可以辣!!!!真是妙极辣!!!!!

下面是代码:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #define maxn 100010
 6 #define mod 1000000007
 7 #define ll long long
 8 using namespace std;
 9 int f[21][maxn],n,m,cnt;ll Ans=9;
10 int find(int x,int y){if(f[y][x]!=x)f[y][x]=find(f[y][x],y);return f[y][x];}
11 void merge(int x,int y,int len){
12   if(find(x,len)!=find(y,len))
13     f[len][f[len][x]]=f[len][y];
14 }
15 int main(){
16     //freopen("a.in","r",stdin);
17     scanf("%d%d",&n,&m);for(int j=0;j<=20;++j)for(int i=1;i<=n;++i)f[j][i]=i;
18   for(int i=1;i<=m;++i){
19     int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
20     for(int j=20;j!=-1;j--)
21       if(a+(1<<j)-1<=b)merge(a,c,j),a+=1<<j,c+=1<<j;
22   }
23   for(int j=20;j;j--)
24     for(int i=1;i+(1<<j)-1<=n;++i)
25       merge(i,find(i,j),j-1),
26     merge(i+(1<<(j-1)),f[j][i]+(1<<(j-1)),j-1);
27   for(int i=1;i<=n;++i)if(find(i,0)==i)cnt++;
28   for(int i=1;i<cnt;++i)Ans*=10,Ans%=mod;cout<<Ans;
29   return 0;
30 }

 

标签:10,le,P3295,萌萌,ri2,len,int,li1,SCOI2016
来源: https://www.cnblogs.com/DZN2004/p/12827466.html