【无码专区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"; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【无码专区2】序列划分(数学)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【无码专区1】简单路径的第二大边权(启发
- 下一篇: 腾达路由器设置请问如何设置腾达路由器