P3295 [SCOI2016]萌萌哒
题目传送门
https://www.luogu.com.cn/problem/P3295
分析
我们用ST表,f[i,j]表示[i,i+2^j-1]这一段。
那么初始时每一段单独成一个集合。
对于一个限制可以拆成log 份,然后进行集合合并。
然后呢,如果任意ST[s,t]和ST[i,j]属于同一集合,那么ST[s,t-1]与ST[i,j-1]以及ST[s+2^(t-1)-1,t-1]和f[i+2^(j-1)-1,j-1]都应该属于同一集合。
为了满足这个限制,只要最后再一层一层的做,把下一层的合并了即可。
合并注意必须让编号大的合进编号小的里。
答案就很容易搞出来,也就是9*10^(集合个数-1)。
代码
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=1e5+10,maxm=20; const int mod=1e9+7; int n,m,l1,l2,r1,r2,cnt=0; int fa[maxn*maxm],S[maxn][maxm],sta[maxn*maxm]; int Find(int x) { if(fa[x]==x) return x; return fa[x]=Find(fa[x]); } void merge(int l1,int l2,int k){ int x=Find(S[l1][k]); int y=Find(S[l2][k]); if(x>y) swap(x,y); fa[y]=x; } int main() { scanf("%d%d",&n,&m); if(n==1) printf("10\n"); else { for(int j=0;j<=maxm;j++) for(int i=1;i+(1<<j)-1<=n;i++){ S[i][j]=++cnt; sta[cnt]=i; fa[cnt]=cnt; } for(int i=1;i<=m;i++){ scanf("%d%d%d%d",&l1,&r1,&l2,&r2); for(int j=maxm; j>=0; j--) if(l1+(1<<j)-1<=r1){ merge(l1,l2,j); l1+=(1<<j); l2+=(1<<j); } } for(int j=maxm;j>=1;j--) for(int i=1;i+(1<<j)-1<=n;i++){ int f=Find(S[i][j]); int s=sta[f]; merge(i,s,j-1); merge(i+(1<<j-1),s+(1<<j-1),j-1); } int ans=9; for(int i=2;i<=n;i++) if(fa[S[i][0]]==S[i][0]) ans=(long long)ans*10%mod; printf("%d\n",ans); } return 0; }
$Never\ Give\ Up$