BZOJ4569: [Scoi2016]萌萌哒

Description

一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条
件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...S
r2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,13
1141不满足条件,前者数的长度不为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

1 2 3 4
3 3 3 3

Sample Output

90
 
好巧妙的思路,我心服口服了。
不难发现每一次操作等价于在两个区间内依次合并连通分量,如果暴力合并显然是O(NM)的。
怎么办呢?我们对序列建立ST表,这样就可以理解成有nlogn个点,合并时先合并大块,再递归合并小块。注意每一个点只参与一次合并,总时间复杂度仍为O(MlogN)。
不知道为什么按秩合并反而快一点。
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
	if(head==tail) {
		int l=fread(buffer,1,BufferSize,stdin);
		tail=(head=buffer)+l;
	}
	return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
const int maxn=100010;
int n,m,A[maxn],pa[maxn*20],s[maxn*20],Log[maxn];
int id(int k,int x) {return k*n+x;}
int findset(int x) {return pa[x]==x?x:findset(pa[x]);}
void merge(int x,int y,int k) {
	int f1=findset(id(k,x)),f2=findset(id(k,y));
	if(f1!=f2) {
		if(s[f1]>s[f2]) swap(f1,f2);
		pa[f1]=f2;if(s[f1]==s[f2]) s[f2]++;
		if(k) merge(x,y,k-1),merge(x+(1<<k-1),y+(1<<k-1),k-1);
	}
}
int main() {
	n=read();m=read();
	for(int i=0;(1<<i)<=n;i++) rep(j,1,n) pa[id(i,j)]=id(i,j),s[id(i,j)]=1;
	Log[0]=-1;rep(i,1,n) Log[i]=Log[i>>1]+1;
	rep(i,1,m) {
		int a=read(),b=read(),c=read(),d=read(),k=Log[b-a+1];
		merge(a,c,k);merge(b-(1<<k)+1,d-(1<<k)+1,k);
	}
	int cnt=0;
	rep(i,1,n) A[i]=findset(id(0,i));
	sort(A+1,A+n+1);
	rep(i,1,n) if(A[i]!=A[i-1]) cnt++;
	if(cnt==1) puts("10");
	else {
		ll ans=9;
		rep(i,1,cnt-1) (ans*=10)%=1000000007;
		printf("%lld\n",ans);
	}
	return 0;
}

  

posted @ 2016-05-27 15:36  wzj_is_a_juruo  阅读(174)  评论(0编辑  收藏  举报