0%

「LOJ 2014」「SCOI2016」萌萌哒

LOJ #2014. 「SCOI2016」萌萌哒

题意

有一个没有前导零的 $n$ 位十进制数 $S_1 S_2\dotsc S_n$,$m$ 条限制,一条限制形如 $S_{l_1}S_{l_1+1}\dotsc S_{r_2}$ 与 $S_{l_2}S_{l_2+1}\dotsc S_{r_2}$ 这两个子串需要完全相同

问有多少种合法的方案

模 $10^9+7$

做法

考虑对应位连边,假设最后有 $k$ 个联通块,方案数就是 $9\times 10^{k-1}$

连边数量是 $\mathcal O(n^2)$ 的,考虑使用倍增优化连边

点 $p_{i,j}$ 代表了以 $j$ 为左端点,长度为 $2^i$ 的区间,$p_{i,j}$ 和 $p_{i,k}$ 之间有边相当于对应的每个点之间有边

这样每个限制连边的数量是 $\mathcal O(\log n)$ 的

然后从大到小枚举 $i$,同一层的边可以用并查集去重,保留有效的至多 $n-1$ 条边

一条 $p_{i,j}\leftrightarrow p_{i,k}$ 的边下传到第 $i-1$ 层变成 $p_{i-1,j}\leftrightarrow p_{i-1,k}$ 和 $p_{i-1,j+2^{i-1}}\leftrightarrow p_{i-1,k+2^{i-1}}$ 的边

因此这种实现的总复杂度是 $\mathcal O(n\log^2 n)$ 的

可以轻易做到 $\mathcal O(n\log n)$ 吧

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<string.h>
#include<cmath>
#include<vector>

using namespace std;
#define ll long long

inline char read() {
static const int IN_LEN = 1000000;
static char buf[IN_LEN], *s, *t;
return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x) {
static bool iosig;
static char c;
for (iosig=false, c=read(); !isdigit(c); c=read()) {
if (c == '-') iosig=true;
if (c == -1) return;
}
for (x=0; isdigit(c); c=read()) x=((x+(x<<2))<<1)+(c^'0');
if (iosig) x=-x;
}

const int N = 100005, P = 1000000007;
int n, m, f[N];
vector<pair<int,int>> ans, a[N];
int find(int x){ return f[x]==x?x:f[x]=find(f[x]);}
int main() {
read(n), read(m);
while(m--){
static int l1, r1, l2, r2, len;
read(l1), read(r1), read(l2), read(r2), len=r1-l1+1;
for(int i=16; ~i; --i) if(len>>i&1)
a[i].push_back(make_pair(l1, l2)), l1+=1<<i, l2+=1<<i;
}
for(int i=1; i<=n; ++i) f[i]=i;
for(int i=16; ~i; --i){
for(auto j:a[i]) if(find(j.first)!=find(j.second))
f[f[j.first]]=f[j.second], ans.push_back(j);
if(i) for(auto j:ans)
a[i-1].push_back(j),
a[i-1].push_back(make_pair(j.first+(1<<i>>1), j.second+(1<<i>>1)));
}
int Ans=9;
for(int i=1; i<n-ans.size(); ++i) Ans=Ans*10ll%P;
return printf("%d", Ans), 0;
}