【无码专区2】序列划分(数学)
有std,但是没有自我实现,所以是无码专区
description
完全由数字组成的字符串 sss,划分成若干段,每一段看成一个十进制的数(允许前导零)求有多少种划分方法使得相邻两个数至少一个是 DDD 的倍数。对 109+710^9+7109+7 取模。
数据:5×105,1065\times 10^5,10^65×105,106 级别。
我的想法
针对 30%30\%30% 的数据:n≤1000n\le 1000n≤1000 【n=∣s∣n=|s|n=∣s∣】。
设 dpi,0/1:dp_{i,0/1}:dpi,0/1: 划分在 iii 处,枚举上一个划分点 jjj,判断 (j,i](j,i](j,i] 是否是 DDD 的倍数 的方案数。
然后直接转移,就是 O(n2)O(n^2)O(n2) 的。
solution
对于另 20%:(D,10)=120\%:(D,10)=120%:(D,10)=1 就是提示正解的部分分。
记 Si=(∑si⋅10n−i)%DS_i=(\sum_{s_i}·10^{n-i})\%DSi=(∑si⋅10n−i)%D,即以 iii 开始的后缀对应的十进制数(在取模 DDD 的意义下)
判定条件: s[i,j]s[i,j]s[i,j] 组成的数能被 DDD 整除,当且仅当 Si=Sj+1S_{i}=S_{j+1}Si=Sj+1。
设 dpi,0/1:dp_{i,0/1}:dpi,0/1: 从前往后划分到 iii ,当前段能否被 DDD 整除的方案数。
如果能被 DDD 整除,那么必须从 Sj=Si+1,1≤j<iS_j=S_{i+1},1\le j<iSj=Si+1,1≤j<i 转移过来,否则从不被整除的 SjS_jSj 转移过来。
用一个桶数组 cnti:Sj=icnt_i:S_j=icnti:Sj=i 的 dpjdp_jdpj 的和。
i.e. 在 iii 转移被整除的信息的时候,相当于去找 cntSicnt_{S_i}cntSi 这个桶,dpi→cntSidp_i\rightarrow cnt_{S_i}dpi→cntSi。
以上是 (D,10)=1(D,10)=1(D,10)=1,当 (D,10)≠1(D,10)\neq 1(D,10)=1 时,上述的判定条件就不完全对了。
改写 D=D1⋅D2D=D_1·D_2D=D1⋅D2,其中 (D1,10)=1,D2=2a5b(D_1,10)=1,D_2=2^a5^b(D1,10)=1,D2=2a5b。
改写 Si:S_i:Si: 从 iii 开始到结尾形成的十进制数对 D1D_1D1 取模的结果。
改写判定条件:s[i,j]s[i,j]s[i,j] 组成的数能被 DDD 整除,当且仅当 Si=Sj+1∧D2∣s[i,j]S_i=S_{j+1}\wedge D_2\Big |s[i,j]Si=Sj+1∧D2∣∣∣s[i,j]。
在十进制下 s[i,j]s[i,j]s[i,j] 要能被 2,52,52,5 的幂次整除,只需要看后面的若干位即可。
这里 D≤106≈220≈59D\le 10^6\approx 2^{20}\approx 5^9D≤106≈220≈59,所以最多看后面的 202020 位。
具体而言
- 首先判断 s[i−19,i]s[i-19,i]s[i−19,i] 组成的数是否 DDD 的倍数。
- 是,再从桶里面找 cntSi+1→dpicnt_{S_{i+1}}\rightarrow dp_icntSi+1→dpi。
- 暴力处理 s[j,i],i−19≤j≤i−1s[j,i],i-19\le j\le i-1s[j,i],i−19≤j≤i−1 判断是否是 DDD 的倍数,再 →dpi\rightarrow dp_i→dpi 。
- 对于 j=i−19j=i-19j=i−19 到 iii 中间已经包含有 202020 位了【i−(i−19)+1=20i-(i-19)+1=20i−(i−19)+1=20】,所以 Sj→cntS_j\rightarrow cntSj→cnt,可以入桶了。
参考code
#include <bits/stdc++.h>
using namespace std;
template <typename T>
T power(T a, long long b) {
T r = 1;
while (b) {
if (b & 1) {
r *= a;
}
a *= a;
b >>= 1;
}
return r;
}
template <typename T>
T inverse(T a, T m) {
a %= m;
if (a < 0) {
a += m;
}
T b = m, u = 0, v = 1;
while (a) {
T t = b / a;
b -= a * t;
swap(a, b);
u -= v * t;
swap(u, v);
}
if (u < 0) {
u += m;
}
return u;
}
template <int _P>
struct modnum {
static constexpr int P = _P;
private:
int v;
public:
modnum() : v(0) {
}
modnum(long long _v) {
v = _v % P;
if (v < 0) {
v += P;
}
}
explicit operator int() const {
return v;
}
bool operator==(const modnum &o) const {
return v == o.v;
}
bool operator!=(const modnum &o) const {
return v != o.v;
}
modnum inverse() const {
return modnum(::inverse(v, P));
}
modnum operator-() const {
return modnum(v ? P - v : 0);
}
modnum operator+() const {
return *this;
}
modnum &operator++() {
v++;
if (v == P) {
v = 0;
}
return *this;
}
modnum &operator--() {
if (v == 0) {
v = P;
}
v--;
return *this;
}
modnum operator++(int) {
modnum r = *this;
++*this;
return r;
}
modnum operator--(int) {
modnum r = *this;
--*this;
return r;
}
modnum &operator+=(const modnum &o) {
v += o.v;
if (v >= P) {
v -= P;
}
return *this;
}
modnum operator+(const modnum &o) const {
return modnum(*this) += o;
}
modnum &operator-=(const modnum &o) {
v -= o.v;
if (v < 0) {
v += P;
}
return *this;
}
modnum operator-(const modnum &o) const {
return modnum(*this) -= o;
}
modnum &operator*=(const modnum &o) {
v = (int) ((long long) v * o.v % P);
return *this;
}
modnum operator*(const modnum &o) const {
return modnum(*this) *= o;
}
modnum &operator/=(const modnum &o) {
return *this *= o.inverse();
}
modnum operator/(const modnum &o) const {
return modnum(*this) /= o;
}
};
template <int _P>
ostream &operator<<(ostream &out, const modnum<_P> &n) {
return out << int(n);
}
template <int _P>
istream &operator>>(istream &in, modnum<_P> &n) {
long long _v;
in >> _v;
n = modnum<_P>(_v);
return in;
}
using num = modnum<1000000007>;
int main() {
freopen("division.in", "r", stdin);
freopen("division.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
string s;
int d;
cin >> s >> d;
int e = 1;
while (d % 2 == 0) {
d /= 2;
e *= 2;
}
while (d % 5 == 0) {
d /= 5;
e *= 5;
}
int n = s.size();
vector<int> a(n + 1);
for (int i = 0; i < n; ++i) {
a[i + 1] = (a[i] * 10 + (s[i] - '0')) % (d * e);
}
int inv10 = d == 1 ? 0 : inverse(10, d);
vector<int> pw(n + 1);
pw[0] = 1;
for (int i = 0; i < n; ++i) {
pw[i + 1] = (long long) pw[i] * inv10 % d;
}
vector<int> base(20);
base[0] = 1;
for (int i = 0; i + 1 < 20; ++i) {
base[i + 1] = base[i] * 10 % (d * e);
}
vector<num> foo(n + 1), bar(n + 1);
vector<num> offset_foo(d), offset_bar(d);
bar[0] = 1;
num pref = 0;
for (int i = 0; i <= n; ++i) {
foo[i] += pref;
if (a[i] % e == 0) {
foo[i] += offset_foo[(long long) a[i] * pw[i] % d];
bar[i] += offset_bar[(long long) a[i] * pw[i] % d];
}
pref += bar[i];
offset_foo[(long long) a[i] * pw[i] % d] -= bar[i];
offset_bar[(long long) a[i] * pw[i] % d] += foo[i] + bar[i];
for (int j = i + 1; j <= n && j < i + 20; ++j) {
if ((a[j] - (long long) a[i] * base[j - i]) % (d * e) == 0) {
foo[j] -= bar[i];
bar[j] += foo[i] + bar[i];
}
if (a[j] % e == 0 && (long long) a[i] * pw[i] % d == (long long) a[j] * pw[j] % d) {
foo[j] += bar[i];
bar[j] -= foo[i] + bar[i];
}
}
}
cout << foo[n] + bar[n] << "\n";
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)