1. 程式人生 > >[SCOI2016]萌萌噠

[SCOI2016]萌萌噠

1=1 dig IT 技術 src -- while esp 結果

題目描述

一個長度為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,後者第二位與第五位不同。問滿足以上所有條件的數有多少個。

輸入輸出格式

輸入格式:

第一行兩個數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。

輸出格式:

一個數,表示滿足所有條件且長度為n的大數的個數,答案可能很大,因此輸出答案模10^9+7的結果即可。

輸入輸出樣例

輸入樣例#1:
4 2
1 2 3 4
3 3 3 3
輸出樣例#1:
90
正解:
首先想到對於相同的數可以放到一個集合裏,用並查集維護
有多少個集合(假設有cnt個集合)
那麽最後答案就是ans=9*(10^(cnt-1))%mod
證明:第一個位置數字可以為9種(不能為0)
     第二個位置數字可以為10種(0-9)
第三個位置數字可以為10種(0-9)
.........
一共有cnt個位置,即ans;
但是暴力的話,很明顯會超時,怎們辦呢?
我們可以用st表維護。
某個區間看成一個節點,他的兩個兒子拼起來即為自身區間
(按照倍增的區間分法)
當進行合並的時候,我們對區間進行合並而不是點。
最後,我們按照區間長度從大到小依次考察每個區間,
如果他並不是一個獨立的區間,那麽我們就把他的限制
放給他的兒子,即合並操作,直到區間長度為1
到最後統計一下集合的個數就可以得出答案

技術分享圖片

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<string>
 6 #include<cmath>
 7 #define ll long long
 8 #define DB double
 9 #define inf 214748360000
10 #define mod 1000000007
11 using namespace std;
12 inline int
read() 13 { 14 int x=0,w=1;char ch=getchar(); 15 while(!isdigit(ch)){if(ch==-) w=-1;ch=getchar();} 16 while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-0,ch=getchar(); 17 return x*w; 18 } 19 const int N=1e5+90; 20 int n,fa[N*18],m,cnt,ch[N*18][2]; 21 int f[N][18]; 22 int F(int x) 23 { 24 if(x==fa[x]) return x; 25 else return fa[x]=F(fa[x]); 26 } 27 void merge(int x,int y) 28 { 29 int fx=F(x),fy=F(y); 30 fa[fx]=fa[fy]=fx; 31 } 32 int main() 33 { 34 n=read();m=read(); 35 for(int j=0;(1<<j)<=n;++j) 36 for(int i=1;i+(1<<j)-1<=n;++i) 37 { 38 f[i][j]=++cnt;fa[cnt]=cnt; 39 if(j) 40 { 41 ch[cnt][0]=f[i][j-1]; 42 ch[cnt][1]=f[i+(1<<(j-1))][j-1]; 43 } 44 } 45 while(m--) 46 { 47 int l1,r1,l2,r2; 48 l1=read();r1=read();l2=read();r2=read(); 49 int j=log2(r2-l2+1); 50 merge(f[l1][j],f[l2][j]); 51 merge(f[r1-(1<<j)+1][j],f[r2-(1<<j)+1][j]); 52 } 53 for(int i=cnt;i>n;--i) 54 { 55 int t=F(i); 56 if(t!=i) 57 { 58 merge(ch[i][0],ch[t][0]); 59 merge(ch[i][1],ch[t][1]); 60 } 61 } 62 cnt=0; 63 for(int i=1;i<=n;++i) 64 cnt+=(F(i)==i); 65 int ans=9; 66 for(int i=1;i<=cnt-1;++i) 67 ans=1ll*ans*10%mod; 68 printf("%d",ans); 69 return 0; 70 }
View Code
走到懸崖處,想辦法,就有路了(搭個梯子??)。

[SCOI2016]萌萌噠