您的位置:首页 > 其它

bzoj4569 [Scoi2016]萌萌哒

2017-08-01 09:14 211 查看

Description

一个长度为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 3

Sample Output

90

正解:$ST$表+并查集。
首先我们可以想到一个$O(n^{2})$的暴力,那就是把所有相等的数用并查集弄起来,最后统计联通块个数计算答案就行了。
然后考虑如何优化。本能反应是想到线段树,但是线段树是没有办法维护的。
那么我们可以考虑$ST$表,虽然我也不知道怎么想到这一步。。
如果两个区间要求相等,那么我们就把每个区间分成两个区间,然后分别用并查集连起来。
最后我们直接把并查集的父亲标记下放,在最底层统计连通块个数就行了。

//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define rhl (1000000007)
#define inf (1<<30)
#define N (100010)
#define il inline
#define RG register
#define ll long long
#define pos(i,j) (((i)-1)*20+(j)+1)

using namespace std;

int fa[22*N],vis[22*N],leg
,n,m,tot;

il int gi(){
RG int x=0,q=1; RG char ch=getchar();
while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if (ch=='-') q=-1,ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
return q*x;
}

il ll qpow(RG ll a,RG ll b){
RG ll ans=1;
while (b){
if (b&1) ans=ans*a%rhl;
a=a*a%rhl,b>>=1;
}
return ans;
}

il int find(RG int x){ return fa[x]==x ? x : fa[x]=find(fa[x]); }

il void unionn(RG int u,RG int v,RG int leg){
RG int x=find(pos(u,leg)),y=find(pos(v,leg));
if (x!=y) fa[x]=y; return;
}

int main(){
#ifndef ONLINE_JUDGE
freopen("mmd.in","r",stdin);
freopen("mmd.out","w",stdout);
#endif
n=gi(),m=gi(); for (RG int i=2;i<=n;++i) leg[i]=leg[i>>1]+1;
for (RG int j=0;j<=19;++j) for (RG int i=1;i<=n;++i) fa[pos(i,j)]=pos(i,j);
for (RG int i=1,l1,r1,l2,r2,len;i<=m;++i){
l1=gi(),r1=gi(),l2=gi(),r2=gi(),len=r1-l1+1,unionn(l1,l2,leg[len]);
unionn(r1-(1<<leg[len])+1,r2-(1<<leg[len])+1,leg[len]);
}
for (RG int j=19;j;--j)
for (RG int i=1,x;i<=n;++i)
if (i+(1<<j)-1<=n && find(pos(i,j))!=pos(i,j)){
x=(find(pos(i,j))-1)/20+1,fa[find(pos(i,j-1))]=find(pos(x,j-1));
fa[find(pos(i+(1<<(j-1)),j-1))]=find(pos(x+(1<<(j-1)),j-1));
}
for (RG int i=1;i<=n;++i) if (!vis[find(pos(i,0))]) vis[find(pos(i,0))]=1,++tot;
printf("%lld\n",9*qpow(10,tot-1)%rhl); return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: