*题解:P3295 [SCOI2016] 萌萌哒
解析
法 1
倍增并查集。
区间相同意味着对应字符相同,故可以使用并查集维护相等关系,把相同的字符并在一起。记最终连通块个数为 \(x\),则答案为 \(9 \times 10 ^ {x - 1}\),注意最高位不能是 \(0\)。
然而这样复杂度爆炸。不算并查集复杂度,我们现在是总共 \(O(n ^ 2)\) 处理限制条件,\(O(n)\) 找连通块个数。考虑把两个操作都平衡到 \(O(n\log n)\),这时就需要使用倍增并查集。
在此题中,倍增和并查集结合指的是用并查集来倍增地维护区间信息。具体地,仿照 ST 表,我们可以将一个区间拆成两个长度为 \(2 ^ i\) 的区间,用并查集来维护每一层的区间。这两个区间可能有交,不过不影响维护。对于限制条件给出的两个区间,我们可以将它们分别拆分,并用并查集将 \([l_1,l_1 + 2 ^ i - 1]\),\([l_2,l_2 + 2 ^ i - 1]\) 并起来表示这两个区间相等,右半边同理。查询时将大区间的连通信息下放,左半边连向并查集中父亲的左半边,右半边连向父亲的右半边即可。
若并查集但此操作复杂度为 \(O(\log n)\),则总时间复杂度 \(O(n \log ^ 2 n + m \log n)\)。
法 2
线段树维护哈希。
考虑直接用并查集维护相等字符为什么不行,发现是因为做了很多无效合并,假如每次都会合并两个未连通的连通块,那么合并过后连通块个数 \(-1\),最终只会合并 \(O(n)\) 次。想要去除无效合并,就要跳过已知相同的字符,找到第一个不相同(不连通)的字符,这个问题可以使用二分加哈希来解决,具体地,初始根据每个位置下标计算哈希值,每次找到一个不相同的位置就将它们所在连通块内的位置的哈希值设为一样的,这个操作可以通过启发式合并来保证复杂度。于是我们需要一个支持单点修改区间查询的数据结构,线段树满足这个条件。
时间复杂度 \(O((n + m)\log^2 n)\),注意常数。
代码
法 1
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N = 1e5 + 5,M = 20 + 5,mod = 1e9 + 7;
int fa[M][N];
bool vis[N];
int find(int k,int x){
if(fa[k][x] != x) fa[k][x] = find(k,fa[k][x]);
return fa[k][x];
}
void merge(int k,int a,int b){
int p = find(k,a),q = find(k,b);
fa[k][p] = q;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=0;j<M;j++){
fa[j][i] = i;
}
}
while(m--){
int l1,l2,r1,r2;
cin>>l1>>r1>>l2>>r2;
int k = __lg(r1 - l1 + 1);
merge(k,l1,l2),merge(k,r1 - (1 << k) + 1,r2 - (1 << k) + 1);
}
for(int i=M - 1;i>=1;i--){
for(int j=1;j + (1 << i) - 1<=n;j++){
int f = find(i,j);
merge(i - 1,j,f),merge(i - 1,j + (1 << (i - 1)),f + (1 << (i - 1)));
}
}
int cnt = 0;
for(int i=1;i<=n;i++){
if(!vis[find(0,i)]){
vis[fa[0][i]] = true;
cnt++;
}
}
int res = 9;
for(int i=1;i<cnt;i++){
res = 1ll * res * 10 % mod;
}
cout<<res;
return 0;
}
法 2
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef pair<int,int> pii;
typedef unsigned int ull;
typedef pair<ull,int> pui;
const int N = 1e5 + 5,M = 20 + 5,base = 13331,mod = 1e9 + 7;
ull hsh[N << 2],now[N];
ull mi[N];
void push_up(int p,int l,int r){
hsh[p] = hsh[ls(p)] * mi[r - mid] + hsh[rs(p)];
}
void build(int p,int l,int r){
if(l == r){
hsh[p] = l;
return;
}
build(ls(p),l,mid),build(rs(p),mid + 1,r);
push_up(p,l,r);
}
void modi(int p,int l,int r,int x,int k){
if(l == x && r == x){
hsh[p] = k;
return;
}
if(x <= mid){
modi(ls(p),l,mid,x,k);
}else{
modi(rs(p),mid + 1,r,x,k);
}
push_up(p,l,r);
}
ull ask(int p,int l,int r,int L,int R){
if(l >= L && r <= R){
return hsh[p];
}
int len = max(0,min(R,r) - mid);
if(mid < L){
return ask(rs(p),mid + 1,r,L,R);
}else if(mid + 1 > R){
return ask(ls(p),l,mid,L,R);
}else{
return ask(ls(p),l,mid,L,R) * mi[len] + ask(rs(p),mid + 1,r,L,R);
}
}
int fa[N],vis[N],siz[N];
vector<int> v[N];
int find(int x){
if(fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
void merge(int a,int b){
int p = find(a),q = find(b);
if(siz[p] >= siz[q]) swap(p,q);
fa[p] = q;
siz[q] += siz[p];
}
int read(){
int a = 1,x = 0;
char ch = getchar();
while(ch > '9' || ch < '0'){
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return a * x;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stderr);
mi[0] = 1;
for(int i=1;i<N;i++){
mi[i] = mi[i - 1] * base;
}
int n = read(),m = read();
build(1,1,n);
for(int i=1;i<=n;i++){
v[i].push_back(i);
fa[i] = i;
siz[i] = 1;
}
while(m--){
int l1 = read(),r1 = read(),l2 = read(),r2 = read();
while(l1 <= r1){
int l = l1,r = r1 + 1;
while(l < r){
int md = (l + r) >> 1;
int len = md - l1 + 1;
if(ask(1,1,n,l1,md) != ask(1,1,n,l2,l2 + len - 1)) r = md;
else l = md + 1;
}
if(l > r1) break;
int p2 = l2 + l - l1;
int fl = find(l),fp = find(p2);
if(fl != fp){
if(siz[fl] < siz[fp]){
ull val = fp;
for(int i : v[fl]){
modi(1,1,n,i,val);
v[fp].push_back(i);
}
vector<int>().swap(v[fl]);
}else{
ull val = fl;
for(int i : v[fp]){
modi(1,1,n,i,val);
v[fl].push_back(i);
}
vector<int>().swap(v[fp]);
}
merge(l,p2);
}
l2 = p2 + 1;
l1 = l + 1;
}
}
int res = 9,cnt = 0;
for(int i=1;i<=n;i++){
if(!vis[find(i)]){
vis[fa[i]] = true;
cnt++;
}
}
for(int i=1;i<cnt;i++){
res = 1ll * res * 10 % mod;
}
cout<<res;
return 0;
}

浙公网安备 33010602011771号