luogu P3295 [SCOI2016]萌萌哒 |倍增+并查集
题目描述
一个长度为 nnn 的大数,用 S1S2S3⋯SnS_1S_2S_3 \cdots S_nS1S2S3⋯Sn表示,其中 SiS_iSi 表示数的第 iii 位, S1S_1S1 是数的最高位。告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2l_1,r_1,l_2,r_2l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2⋯Sr1S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}Sl1Sl1+1Sl1+2⋯Sr1与Sl2Sl2+1Sl2+2⋯Sr2S_{l_2}S_{l_2+1}S_{l_2+2} \cdots S_{r_2}Sl2Sl2+1Sl2+2⋯Sr2完全相同。
比如 n=6n=6n=6 时,某限制条件 l1=1,r1=3,l2=4,r2=6l_1=1,r_1=3,l_2=4,r_2=6l1=1,r1=3,l2=4,r2=6 ,那么 123123123123123123,351351351351351351 均满足条件,但是 120121201212012,131141131141131141 不满足条件,前者数的长度不为 666 ,后者第二位与第五位不同。问满足以上所有条件的数有多少个。
输入格式
第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。
接下来m行,对于第i行,有4个数 li1,ri1,li2,ri2,分别表示该限制条件对应的两个区间。
1≤n≤1051\le n\le 10^51≤n≤105,1≤m≤1051\le m\le 10^51≤m≤105 ,1≤li1,ri1,li2,ri2≤n 1\le li1,ri1,li2,ri2 \le n 1≤li1,ri1,li2,ri2≤n ;并且保证 ri1−li1=ri2−li2 ri1-li1=ri2-li2 ri1−li1=ri2−li2 。
输出格式
一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模 109+7 10^9+7 109+7 的结果即可。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int _=1e5+10,N=16,p=1e9+7,mod=1e9+7;
#define int long long
int n,m,f[22][_];
int get(int x,int y){
return f[y][x]==x?x:f[y][x]=get(f[y][x],y);
}
bool vis[_];
inline int ksm(int x,int y){
int res=1;
while(y){
if(y&1)res=res*x%mod;
x=x*x%mod; y>>=1;
}
return res;
}
void merge(int x,int y,int len){
if(get(x,len)!=get(y,len))
f[len][f[len][x]]=f[len][y];
}
signed main(){
cin>>n>>m;
for(int j=0;j<=21;j++)for(int i=1;i<=n;i++)f[j][i]=i;
int l1,l2,r1,r2;
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
for(int j=20;j>=0;j--)
if(l1+(1<<j)-1<=r1)merge(l1,l2,j),l1+=1<<j,l2+=1<<j;
}
int ans=9,cnt=0;
for(int j=20;j;j--)
for(int i=1;i+(1<<j)-1<=n;i++)
{merge(i,get(i,j),j-1);merge(i+(1<<(j-1)),f[j][i]+(1<<(j-1)),j-1);}
for(int i=1;i<=n;i++)if(get(i,0)==i)cnt++;
for(int i=1;i<cnt;i++)ans*=10,ans%=mod;
cout<<ans<<endl;
}