bzoj 4569 [Scoi2016]萌萌噠
阿新 • • 發佈:2017-10-20
wap 連通塊 font 有一點 當當 names 因此 cstring geo
[Submit][Status][Discuss]
1141不滿足條件,前者數的長度不為6,後者第二位與第五位不同。問滿足以上所有條件的數有多少個。
Sample Input
4 2
1 2 3 4
3 3 3 3
4569: [Scoi2016]萌萌噠
Time Limit: 10 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
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,13Input
第一行兩個數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 21 2 3 4
3 3 3 3
Sample Output
90Solution
如果沒有限制,沒有模數的話,答案應該是9*10n,加了限制之後,有些數可以由其它數確定,相當於位數減少了
我們需要確定有效的位數
可以用並查集,把一樣的數位看成一個連通塊
但是一個一個修改的話,很慢,是O(mn)的
其實有些限制是沒用的,最多需要修改n-1個數位的fa
所以需要維護很多個不需要修改的區間
可以用一個類似st表的東西,我覺得也有一點像線段樹
fa[i][j]=x表示i為左端點,2j為長度的區間的數,與x為左端點,2j為長度的區間的數對應相等
每次修改的時候,把[l1,r1],[l2,r2]分別轉化為兩個這樣的區間,然後依次遞歸到更小的區間去修改,當當前區間滿足條件,或區間長度為1時停止遞歸
我傻得區間左右端點都算不清了⊙︿⊙
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<queue> #define nn 100011 using namespace std; const long long mod=1e9+7; int fa[nn][25],log_2; int read() { int ans=0,f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch==‘-‘) f=-1;ch=getchar();} while(isdigit(ch)) {ans=ans*10+ch-‘0‘;ch=getchar();} return ans*f; } int find(int x,int l) { return fa[x][l]==x? x:fa[x][l]=find(fa[x][l],l); } void merge(int a,int b,int l) { int f1=find(a,l),f2=find(b,l); if(f1==f2) return; fa[f1][l]=f2; if(!l) return; l--; merge(a,b,l); merge(a+(1<<l),b+(1<<l),l); } int main() { long long res=9; int n,m,l1,r1,l2,r2,len,num=-1; n=read();m=read(); if(n==1) { printf("9"); return 0; } for(int i=1;i<=n;i++) for(int j=0;j<=18;j++) fa[i][j]=i; for(int i=1;i<=m;i++) { l1=read();r1=read();l2=read();r2=read(); len=log2(r1-l1+1); if(l1<l2) swap(l1,l2),swap(r1,r2); merge(l1,l2,len);merge(r1-(1<<len)+1,r2-(1<<len)+1,len); } for(int i=1;i<=n;i++) if(find(i,0)==i) num++; for(int i=1;i<=num;i++) res=res*(long long)10%mod; printf("%lld",res); return 0; } /* 5 3 2 3 1 2 1 5 1 5 4 4 3 3 */
bzoj 4569 [Scoi2016]萌萌噠