bzoj4569-[Scoi2016]萌萌噠
阿新 • • 發佈:2019-01-14
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
4 2
1 2 3 4
3 3 3 3
Sample Output
90
Solution
發現約束條件等價於若干組 \(S_{l_{i1} + d} = S_{l_{i2} + d}\).
這樣就可以用並查集維護, 時間複雜度 \(O(m*n*\alpha (n))\).
考慮每次都是一段連續的區間, 利用倍增優化:
fa[l][p]
表示起點為p, 長度為l的區間的父親;
最後查詢時, 從\(l\) 合併到\(l-1\), 一直到0.
Code
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> using namespace std; #define rep(i,l,r) for(register int i=(l);i<=(r);++i) #define repdo(i,l,r) for(register int i=(l);i>=(r);--i) #define il inline typedef double db; typedef long long ll; //--------------------------------------- const int nsz=1e5+50,lnsz=25; const ll nmod=1e9+7; int n,m,ln,cnt; int fa[lnsz][nsz]; void inituf(){rep(i,0,ln)rep(j,1,n)fa[i][j]=j;} int getfa(int a,int l){return fa[l][a]==a?a:fa[l][a]=getfa(fa[l][a],l);} void merge(int a,int b,int l){ a=getfa(a,l),b=getfa(b,l); if(a!=b)fa[l][a]=b; } void p(){ repdo(i,ln,0){ printf("l=%d ",i); rep(j,1,n)printf("%d ",getfa(j,i)); printf("\n"); } } ll qp(ll a,ll b){ ll res=1; while(b){ if(b&1)res=res*a%nmod; b>>=1,a=a*a%nmod; } return res; } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; ln=22; int a,b,c,d; inituf(); rep(i,1,m){ cin>>a>>b>>c>>d; repdo(j,ln,0){ if(a+(1<<j)-1<=b)merge(a,c,j),a+=(1<<j),c+=(1<<j); } // p(); } repdo(i,ln,1){ repdo(j,n-(1<<i)+1,1){ merge(j,getfa(j,i),i-1); merge(j+(1<<(i-1)),getfa(j,i)+(1<<(i-1)),i-1); } } rep(i,1,n)if(getfa(i,0)==i)++cnt; cout<<9ll*qp(10,cnt-1)%nmod<<'\n'; return 0; }