AtCoder WTF 2019 B Multiple of Nine/南外集训 2024.2.23 T1

给定 \(q\) 个区间 \(\{[l_i, r_i]\}\),计算满足条件的长度为 \(n\) 的十进制数码串 \(S\) 的个数 \(\bmod 10^9+7\)

  • \(\forall i \in [1,q], num(S[l_i, r_i]) \equiv 0\pmod 9\)

其中 \(num(T)\) 表示数码串 \(T\) 代表的整数,\(T[a, b]\) 表示子串 \(T_aT_{a+1}\dots T_b\)。在这道题中,我们允许任意形式的前导零。

\(1\le q\le 15, 1\le n\le 10^9\)

所有涉及到的前缀和位置和 \(0\) 位置是关键位置,我们关心它们的前缀和 \(\bmod 9\) 的结果。假设我们已经知道了这些结果,且满足题中条件,考虑有多少种合法序列符合它。此时可以计算两个相邻的关键位置之间的位置的填法数:假设这两个位置间隔 \(d\),则它们填出来的数有 \(\frac{10^d-1}{9}\) 种可能性 \(\bmod 9 = x\in[1,8]\),而有 \(\frac{10^d-1}{9}+1\) 种可能性 \(\bmod 9 = 0\)。所以我们只关心这两个关键位置的前缀和 \(\bmod 9\) 是否相等。我们先用并查集合并题中给出的区间对应的关键位置,这样只剩下 \(\le q+1\) 个块,接下来再去处理这些块的可能的相等关系。

先计算出所有相邻关键位置前缀和都不同时的答案,然后计算出如果存在某个极大等价类,那么它会让答案乘以多少,这可以用 \(\Theta(q2^q)\) 得到。接下来使用相等关系容斥(实际上就是集合幂级数取 \(\ln\)),得到钦定一个等价类存在的权值,这一部分可以用某些集合幂级数技术做到 \(\Theta(q^22^q)\),但是直接 \(\Theta(3^n)\) 也行。最后进行集合划分 DP,复杂度依然是 \(\Theta(q^22^q)\)\(\Theta(3^q)\)。注意到我们似乎把 \(0\) 位置搞进来了,所以最后需要除以 \(9\),表示 \(0\) 位置的这个连通块前缀和必须被钦定成 \(0\)。还要算上最后一段没被覆盖到的部分,这些位置显然可以任意填。

// Author: kyEEcccccc

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
using ULL = unsigned long long;

#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)

constexpr int N = 16, MOD = 1000000007;

LL kpow(LL x, ULL k = MOD-2)
{
	x = x % MOD;
	LL r = 1;
	while (k)
	{
		if (k & 1) r = r * x % MOD;
		x = x * x % MOD;
		k >>= 1;
	}
	return r;
}

int len, n;
int a[N], b[N];
vector<int> pos;
int ff[N*2], co[N*2], tot_co;
LL tar[1 << N], coef[1 << N], f[1 << N];

int get_anc(int x)
{
	if (x == ff[x]) return x;
	return ff[x] = get_anc(ff[x]);
}

signed main(void)
{
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(nullptr);

	cin >> len >> n;
	pos.assign(1, 0);
	F(i, 0, n-1) cin >> a[i] >> b[i], --a[i], pos.push_back(a[i]), pos.push_back(b[i]);
	sort(pos.begin(), pos.end());
	pos.erase(unique(pos.begin(), pos.end()), pos.end());
	F(i, 0, n-1)
	{
		a[i] = lower_bound(pos.begin(), pos.end(), a[i]) - pos.begin();
		b[i] = lower_bound(pos.begin(), pos.end(), b[i]) - pos.begin();
	}
	F(i, 0, SZ(pos)) ff[i] = i;
	F(i, 0, n-1) ff[get_anc(b[i])] = get_anc(a[i]);
	F(i, 0, SZ(pos)) if (ff[i] == i) co[i] = tot_co++;
	F(i, 0, SZ(pos)) co[i] = co[get_anc(i)];
	assert(tot_co <= N);

	LL xx = 1;
	F(i, 1, SZ(pos))
	{
		int d = pos[i] - pos[i-1];
		LL x = (kpow(10, d) - 1) * kpow(9) % MOD;
		(xx *= x) %= MOD;
	}
	F(w, 1, (1<<tot_co)-1)
	{
		tar[w] = 1;
		F(i, 1, SZ(pos))
		{
			if (!((w >> co[i] & 1) && (w >> co[i-1] & 1))) continue;
			int d = pos[i] - pos[i-1];
			LL x = (kpow(10, d) - 1) * kpow(9) % MOD;
			(tar[w] *= kpow(x) * (x+1) % MOD) %= MOD;
		}
		// cerr << w << ' ' << tar[w] << '\n';
		coef[w] = tar[w];
		int u = __lg(w&-w);
		for (int ww = w^1<<u; ww; ww = (ww - 1) & (w^1<<u))
		{
			(coef[w] -= tar[ww] * coef[w^ww]) %= MOD;
			(f[w] += f[ww] * coef[w^ww] % MOD * 9) %= MOD;
		}
		(f[w] += coef[w] * 9) %= MOD;
	}
	LL ans = f[(1<<tot_co)-1] * xx % MOD * kpow(10, len - pos.back()) % MOD * kpow(9);
	cout << (ans % MOD + MOD) % MOD << '\n';
	
	return 0;
}
posted @ 2024-02-23 13:01  kyEEcccccc  阅读(82)  评论(0编辑  收藏  举报