BZOJ 4569: [Scoi2016]萌萌哒 [并查集 倍增]
题意:长为$n \le 10^5$的数字,给出$m \le 10^5$个限制$[l1,r1]\ [l2,r2]$两个子串完全相等,求方案数
把所有要求相等的位置连起来,不就是$9*10^{连通块个数}$嘛
但是最坏情况要连$nm$次啊
有很多都是重复的太浪费了
于是各种乱搞,甚至想了一下分块,即使能减少边的条数也不能减少计算时会走的边的次数
然后看题解,竟然是用倍增来优化
有道理啊,分块太死板了,倍增的话就可以灵活的得到每个区间
$fa[i][j]$表示从i开始$2^j$个数的区间的父亲(也就是和$fa[i][j]$开始$2^j$个数完全相等)
合并的时候从高层往低层合并(j大到小),遇到fa相同就停下
貌似一次合并最坏也是$O(n)$啊
这里卡了我好久. 每层最多合并$n$次,一共$logn$层,没问题啊
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=1e5+5, P=1e9+7; typedef long long ll; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n, m, l1, r1, l2, r2; int fa[N][18], Log[N]; int find(int i, int j) {return i==fa[i][j] ? i : fa[i][j]=find(fa[i][j], j);} void Union(int x, int y, int j) { int f1=find(x, j), f2=find(y, j); if(f1==f2) return; fa[f1][j]=f2; if(j) Union(x, y, j-1), Union(x + (1<<(j-1)), y+ (1<<(j-1)), j-1); } ll Pow(ll a, int b) { ll ans=1; for(; b; b>>=1, a=a*a%P) if(b&1) ans=ans*a%P; return ans; } int main() { freopen("in","r",stdin); n=read(); m=read(); if(n==1) {puts("10"); return 0;} Log[1]=0; for(int i=2; i<=n; i++) Log[i]=Log[i>>1]+1; for(int i=1; i<=n; i++) for(int j=0; j<=17; j++) fa[i][j]=i; for(int i=1; i<=m; i++) { l1=read(), r1=read(), l2=read(), r2=read(); int t=Log[r1-l1+1]; Union(l1, l2, t); Union(r1-(1<<t)+1, r2-(1<<t)+1, t); } int ans=0; for(int i=1; i<=n; i++) ans += find(i, 0)==i; printf("%lld\n", 9*Pow(10, ans-1)%P); }
Copyright:http://www.cnblogs.com/candy99/