SCOI2016 萌萌噠 題解
阿新 • • 發佈:2021-08-17
實在是一個妙題
我們首先考慮兩個區間完全相等可以轉化為對應點相等,對應的點相等即可以看作他們是一個相同的點。
於是我們有一個暴力:利用並查集,相同的點合併到一起,最後可以得到有多少個並查集。然後,我們可以用簡單的計數知識可以知道最後的答案就是\(9*10^{tot-1}\),因為最高位不能為0。
考慮這樣做的複雜度的預處理在\(O(N^2)\),而查詢答案則是\(O(N)\),複雜度不均衡,我們要考慮一種方法讓他的複雜度都均衡為\(O(N\log N)\)。
於是我們可以發現,並查集的操作有可合併性,我們可以利用倍增實現優化,即:開大約20個並查集,表示某一個點往後\(2^i\)的區間和某一區間相同,這樣我們可以通過倍增完成預處理,再分裂大的並查集為最小的,最後查詢答案,每一步的複雜度都是\(O(N\log N)\)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define int long long using namespace std; #define orz cout<<"lyakioi!!!!!!!!!!!!!!!!!"<<endl inline int r(){int s=0,k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){s=s*10+c-'0';c=getchar();}return s*k;} int fa[3000001],t[1000001][25],n,m,cnt,mp[3000001]; bool b[3000001]; const int mod=1e9+7; int father(int x) { if(fa[x]!=x)fa[x]=father(fa[x]); return fa[x]; } void unit(int x,int y) { int fax=father(x); int fay=father(y); fa[fax]=fay; } int pw(int a,int b) { int ans=1; while(b) { if(b&1)ans*=a,ans%=mod; a*=a; a%=mod; b>>=1; } return ans; } signed main() { n=r();m=r(); int now=1,mx=0; while(1) { if(now>n) { mx--; break; } now*=2;mx++; } for(int i=1;i<=n;i++) { for(int j=0;j<=mx;j++) { t[i][j]=(++cnt); mp[cnt]=i; } } for(int i=1;i<=cnt;i++)fa[i]=i; int l1,r1,l2,r2; for(int i=1;i<=m;i++) { l1=r();r1=r();l2=r();r2=r(); if(l1>l2) { swap(l1,l2); swap(r1,r2); } int k=0,len=r1-l1+1; while(len) { if(len&1) { unit(t[l1][k],t[l2][k]);//? l1+=(1<<k); l2+=(1<<k); } k++; len>>=1; } } // orz; for(int i=mx;i;i--) for(int j=1;j<=n;j++) { int x=t[j][i]; int fax=father(x); if(x==fax)continue; int y=mp[fax];//連往別人 unit(t[j][i-1],t[y][i-1]); unit(t[j+(1<<(i-1))][i-1],t[y+(1<<(i-1))][i-1]); } // orz; int tot=0; for(int i=1;i<=n;i++) { int x=father(t[i][0]); if(!b[x]) { tot++; b[x]=1; } } cout<<9*pw(10,tot-1)%mod; }