P3295 [SCOI2016]萌萌哒
复制下来全是乱码呢QwQ:题目传送门
思路: 我们用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 N=100010,M=20; const int Mod=1000000007; int n,m,l1,l2,r1,r2,cnt=0; int fa[N*M],S[N][M],sta[N*M]; 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<=M; 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=M; j>=0; j--) if(l1+(1<<j)-1<=r1) { merge(l1,l2,j); l1+=(1<<j); l2+=(1<<j); } } for(int j=M ; 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; }