咱們 TM 怎麼又要上文化課。。我 嗶嗶嗶嗶嗶嗶html
有 $T$ 組數據,求c++
$$ \prod_{i = 1}^{n} \prod_{j = 1}^{m} fib[\gcd(i, j)] $$git
$ 1 \leq n, m \leq 10 ^ 6, 1 \leq T \leq 1000 $優化
又是一道莫比烏斯反演套路題。。ui
$$ \begin{aligned} &\prod_{i = 1}^{n} \prod_{j = 1}^{m} fib[\gcd(i, j)]\ &=\prod_{d = 1}^{n} fib[d]^{\sum_{i = 1}^n\sum_{j = 1}^n [(i, j) = d]} \end{aligned} $$spa
寫在冪次那裏很差看,咱們單獨提出來。。。debug
$$ \begin{aligned} & \sum_{i = 1}^n\sum_{j = 1}^n [(i, j) = d]\ &= \sum_{d | x} \mu(\frac x d) \lfloor \frac{n}{x} \rfloor \lfloor \frac{m}{x} \rfloor\ &= \sum_{i = 1} ^ {\lfloor \frac{n}{d} \rfloor} \mu(i) \lfloor \frac{n}{id} \rfloor \lfloor \frac{m}{id} \rfloor \end{aligned} $$code
咱們令 $T = id$ 那麼原式就變成htm
$$ \begin{aligned} &\prod_{d = 1}^{n} fib[d]^{\sum_{i = 1} ^ {\lfloor \frac{n}{d} \rfloor} \mu(i) \lfloor \frac{n}{id} \rfloor \lfloor \frac{m}{id} \rfloor}\ &= \prod_{T = 1}^n (\prod_{d | T} fib[d]^{\mu(\frac{T}{d} )})^{\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor} \end{aligned} $$blog
預處理中間那個括號的內容,而後外面套整除分塊便可。
其實不預處理,套兩層整除分塊 $\mathcal LOJ$ 的機子能夠卡過 QAQ
複雜度是 $\mathcal O(n \log n + T \sqrt n \log n)$ 的。
#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i) #define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0'); return x * fh; } typedef long long ll; int n, m; const int N = 1e6 + 1e3; ll mu[N], prime[N / 4], fib[N], ifib[N], cnt, g[N], ig[N]; bitset<N> is_prime; const int Mod = 1e9 + 7, Pmod = Mod - 1; ll fpm(ll x, ll power) { ll res = 1; for (; power; power >>= 1, (x *= x) %= Mod) if (power & 1) (res *= x) %= Mod; return res; } void Init(int maxn) { is_prime.set(); is_prime[0] = is_prime[1] = false; int res; g[0] = ig[0] = g[1] = ifib[1] = mu[1] = fib[1] = 1; For (i, 2, maxn) { if (is_prime[i]) { prime[++cnt] = i; mu[i] = -1; } For (j, 1, cnt) { res = prime[j] * i; if (res > maxn) break ; is_prime[res] = false; if (i % prime[j]) mu[res] = -mu[i]; else {mu[res] = 0; break ;} } g[i] = 1; fib[i] = fib[i - 1] + fib[i - 2]; if (fib[i] >= Mod) fib[i] -= Mod; ifib[i] = fpm(fib[i], Mod - 2); } For (i, 1, maxn) { for (register int j = i, t = 1; j <= maxn; j += i, ++ t) if (mu[t] == 1) g[j] *= fib[i], g[j] = g[j] >= Mod ? g[j] % Mod : g[j]; else if (mu[t] == -1) g[j] *= ifib[i], g[j] = g[j] >= Mod ? g[j] % Mod : g[j]; (g[i] *= g[i - 1]) %= Mod; ig[i] = fpm(g[i], Mod - 2); } } ll Calc() { cerr << "Calc: " << endl; ll res = 1; ll Nextd, n_, m_; if (n > m) swap(n, m); For(d, 1, n) { n_ = n / d; m_ = m / d; Nextd = min(n / n_, m / m_); (res *= fpm(g[Nextd] * ig[d - 1] % Mod, n_ * m_ % Pmod) ) %= Mod; d = Nextd; } return res; } void File() { #ifdef zjp_shadow freopen ("2000.in", "r", stdin); freopen ("2000.out", "w", stdout); #endif } int main() { File(); Init((int)1e6); int cases = read(); while (cases --) { n = read(); m = read(); printf ("%lld\n", Calc() ); } return 0; }
看我以前的 題解 就好啦。
彷佛說是一個套路題?染上沒有的顏色就是 access
的過程,而後套上線段樹維護就好啦。
求有多少個長度爲 $ n $ 的序列,至少有一個數是質數,序列中的數都是不超過 $ m $ 的正整數,並且這 $ n $ 個數的和是 $ p $ 的倍數。答案對於 $20170408$ 取模。
$ 1 \leq n \leq 10 ^ 9, 1 \leq m \leq 2 \times 10 ^ 7, 1 \leq p \leq 100 $。
看到數據就知道是思博矩冪了,能夠作到 $\mathcal O(m + p^3 \log n)$ 。
其實這是個循環矩陣,無限自乘仍是循環的。咱們只須要維護其中一行就知道其它全部行了,那每次拿個向量搞搞得出一行就行啦。
這樣能夠作到 $\mathcal O(m + p^2 \log n)$ 。
其實那個過程本質上是循環卷積,可是和 $FFT$ 關於 $2^k$ 循環彷佛不太契合。
那麼這個多項式快速冪每次只能作到 $\mathcal O(m + p \log p \log n)$ 。
其實能夠利用 ln
變成加法,而後 exp
回去便可,作到 $\mathcal O(m + p \log p)$ 。
但因爲模數的鬼畜,不能用 NTT
利用 MTT
實現就好啦。。。
顯然個人作法是最暴力的矩冪啦 qwq
#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("2002.in", "r", stdin); freopen ("2002.out", "w", stdout); #endif } const int N = 2e7 + 1e3, M = 110, Mod = 20170408; int n, m, p; bitset<N> is_prime; int prime[N], plen; void Linear_Sieve(int maxn) { is_prime.set(); is_prime[0] = is_prime[1] = false; For (i, 2, maxn) { if (is_prime[i]) prime[++ plen] = i; For (j, 1, plen) { int res = prime[j] * i; if (res > maxn) break; is_prime[res] = false; if (!(i % prime[j])) break; } } } int ptot[M], tot[M]; struct Mat { int a[M][M]; Mat() { Set(a, 0); } inline void Unit() { Rep (i, p) a[i][i] = 1; } inline Mat friend operator * (const Mat &lhs, const Mat &rhs) { Mat res; Rep (i, p) Rep (k, p) Rep (j, p) res.a[i][j] = (res.a[i][j] + 1ll * lhs.a[i][k] * rhs.a[k][j]) % Mod; return res; } }; inline Mat fpm(Mat x, int power) { Mat res; res.Unit(); for (; power; power >>= 1, x = x * x) if (power & 1) res = res * x; return res; } Mat f, trans; int main () { File(); n = read(); m = read(); p = read(); Linear_Sieve(m); For (i, 1, m) { ++ tot[i % p]; if (is_prime[i]) ++ ptot[i % p]; } Rep (i, p) Rep (j, p) trans.a[(i + j) % p][i] += tot[j]; f.a[0][0] = 1; f = fpm(trans, n) * f; int ans = f.a[0][0]; Rep (i, p) Rep (j, p) trans.a[(i + j) % p][i] -= ptot[j]; Set(f.a, 0); f.a[0][0] = 1; f = fpm(trans, n) * f; ans = (ans + Mod - f.a[0][0]) % Mod; printf ("%d\n", ans); return 0; }
#「SDOI2017」新生舞會
左右各有 $n$ 個點的二分圖, $a_{i, j}, b_{i, j}$ 爲 $i, j$ 匹配能獲得的兩個權值。
一個方案中有 $ n $ 對匹配,假設每組匹配的權值 $a_{i, j}$ 分別是 $ a'_1, a'_2, \ldots, a'n $, $b{i, j}$ 分別是 $ b'_1, b'_2, \ldots, b'_n $。令
求一個完美匹配,使得
$$ C = \frac{a'_1 + a'_2 + \cdots + a'_n}{b'_1 + b'_2 + \cdots + b'_n} $$
儘可能大。
這 round1 模板題是真多。。。
直接分數規劃,二分 $C$ ,邊權就變成 $a_{i, j} - C \times b_{i, j}$ 。
那麼就是找一個完美匹配使得匹配的邊權和不小於 $0$ 。
而後用 KM
或者費用流跑最大權匹配就好了。
#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("2003.in", "r", stdin); freopen ("2003.out", "w", stdout); #endif } const int inf = 0x3f3f3f3f; template<int Maxn, int Maxm> struct MCMF { int Head[Maxn], Next[Maxm], to[Maxm], cap[Maxm], e; double val[Maxm]; void Init() { e = 1; Set(Head, 0); } inline void add_edge(int u, int v, double cost, int flow) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = cost; cap[e] = flow; } inline void Add(int u, int v, double cost, int flow) { add_edge(u, v, cost, flow); add_edge(v, u, -cost, 0); } int S, T; int pre[Maxn]; double dis[Maxn]; bitset<Maxn> inq; bool Spfa() { queue<int> Q; For (i, 1, T) dis[i] = - 1e8; Set(pre, 0); inq.reset(); Q.push(S); dis[S] = 1; while (!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false; for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) { if (cap[i] && chkmax(dis[v], dis[u] + val[i])) { pre[v] = i; if (!inq[v]) inq[v] = true, Q.push(v); } } } return pre[T]; } double Run() { int sum_flow = 0; double sum_cost = 0; while (Spfa()) { int flow = inf; for (int u = T; u != S; u = to[pre[u] ^ 1]) chkmin(flow, cap[pre[u]]); for (int u = T; u != S; u = to[pre[u] ^ 1]) { cap[pre[u]] -= flow; cap[pre[u] ^ 1] += flow; sum_cost += val[pre[u]] * flow; } sum_flow += flow; } return sum_cost; } }; const int N = 110; const double eps = 1e-8; MCMF<N * 2, N * N * 2> T; int n; double A[N][N], B[N][N]; #define In(x) (x) #define Out(x) ((x) + n) inline bool check(double k) { T.Init(); T.T = (T.S = Out(n) + 1) + 1; For (i, 1, n) { T.Add(T.S, In(i), 0, 1); T.Add(Out(i), T.T, 0, 1); } For (i, 1, n) For (j, 1, n) T.Add(In(i), Out(j), A[i][j] - k * B[i][j], 1); return T.Run() > 0; } int main () { File(); n = read(); For (i, 1, n) For (j, 1, n) A[i][j] = read(); For (i, 1, n) For (j, 1, n) B[i][j] = read(); double l = 0.0, r = 1e8; while (r - l > eps) { double mid = (l + r) / 2.0; if (check(mid)) l = mid; else r = mid; } printf ("%.6lf\n", l); return 0; }
選出 $ n $ 個同窗, 每一個同窗猜一個長度爲 $ m $ 的序列,當某一個同窗猜的序列在硬幣序列中出現時,就再也不扔硬幣了,而且這個同窗勝利。爲了保證只有一個同窗勝利,同窗們猜的 $ n $ 個序列兩兩不一樣。
最後求出每一個同窗勝利的機率。
$ 1 \leq n, m \leq 300 $
六道題裏惟一沒有那麼思博的題。。
暴力 ac自動機 + 高斯消元 是 $\mathcal O((nm)^3)$ 的,只有 $40$ 分。
考慮優化,顯然咱們最後要求的只有 $n$ 個變量的值,其餘變量都是毫無心義的。
那麼咱們只須要求出這些變量之間的相關方程便可。
咱們多設一個 $f_s$ 爲不匹配到其中任意一個同窗的機率,那麼咱們在當前串後面添加一個同窗 $i$ 的擁有的字符串 $S_i$ 就能對應到它的機率。
這個彷佛利用了一個結論,若是不匹配到 $n$ 個結束節點,停到任意一個點的機率彷佛是相同的。
可是這樣會把 $S_i$ 有個前綴和 $S_j$ 一個後綴是同樣的機率多算上去,咱們減掉就好了,這個出現的機率是 $2^{-l}$ ,$l$ 爲這個前綴的長度。
利用 KMP
實現就能夠作到 $\mathcal O(n^2(n + m))$ 啦。
我懶,用的暴力匹配。。也過啦。。
#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifndef ONLINE_JUDGE freopen ("2004.in", "r", stdin); freopen ("2004.out", "w", stdout); #endif } const int N = 310; double G[N][N]; void Gauss(int n) { For (i, 1, n) { For (j, i + 1, n) if (fabs(G[j][i]) > fabs(G[i][i])) swap(G[i], G[j]); if (!G[i][i]) continue; Fordown (j, n + 1, i) G[i][j] /= G[i][i]; For (j, i + 1, n) Fordown (k, n + 1, i) G[j][k] -= G[j][i] * G[i][k]; } Fordown (i, n, 1) { For (j, 1, i - 1) G[j][n + 1] -= G[j][i] * G[i][n + 1], G[j][i] = 0; } } int n, m; double inv[N]; char str[N][N]; int main() { File(); n = read(); m = read(); For (i, 1, n) scanf ("%s", str[i] + 1); inv[0] = 1.0; For (i, 1, m) inv[i] = inv[i - 1] / 2.0; For (i, 1, n) { G[i][i] = 1.0; G[i][n + 1] = - 1.0; For (j, 1, n) { For (p, 2, m) { int len = 1; while (p + len - 1 <= m && str[i][len] == str[j][p + len - 1]) ++ len; if (p + len == m + 2) G[i][j] += inv[p - 1]; } } } For (i, 1, n) G[n + 1][i] = 1.0; G[n + 1][n + 2] = 1.0; Gauss(n + 1); For (i, 1, n) printf ("%.10lf\n", G[i][n + 2]); return 0; }
一個序列,每一個元素是一個點 $(x_i, y_i)$ 。
有 $m$ 個操做,共有三種:
$\texttt{1 L R}:$ 求 $[L, R]$ 的迴歸直線的斜率。
$\texttt{2 L R S T}:$ 把 $i \in [L, R]$ $x_i$ 加上 $S$ ,$y_i$ 加上 $T$ 。
$\texttt{2 L R S T}:$ 把 $i \in [L, R]$ $x_i$ 變成 $S + i$ ,$y_i$ 變成 $T + i$ 。
$ 1 \leq n, m \leq 10 ^ 5 $
昨天才講的迴歸直線。。今天就用上啦QAQ
$$ \hat{a} = \frac{\sum\limits_{i = L} ^ R (x_i - \bar{x})(y_i - \bar{y})}{\sum\limits_{i = L} ^ R (x_i - \bar{x}) ^ 2} $$
這個數學書上經常用另一種化簡的方式。。
令 $n = R - L + 1$ 即區間長度。
$$ \hat{a} = \frac{(\sum\limits_{i = L} ^ R x_i y_i) - n\bar{x}\bar{y}}{(\sum\limits_{i = L} ^ R x_i^2) - n \bar{x}^2} $$
而後維護區間 $x, y, xy, x^2$ 的和就好了。
#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("2005.in", "r", stdin); freopen ("2005.out", "w", stdout); #endif } const int N = 1e5 + 1e3; using ll = double; ll pre1(ll x) { return x * (x + 1) / 2; } ll pre2(ll x) { return x * (x + 1) * (2 * x + 1) / 6; } #define ls o << 1 #define rs o << 1 | 1 #define mid ((l + r) >> 1) #define lson ls, l, mid #define rson rs, mid + 1, r template<int Maxn> struct Segment_Tree { ll sumxy[Maxn], sumx[Maxn], sumy[Maxn], sumxx[Maxn]; int tag[Maxn], S[Maxn], T[Maxn]; inline void modify(int o, int l, int r, int opt, int sv, int tv) { int len = r - l + 1; chkmax(tag[o], opt); if (opt == 2) { sumxy[o] += sv * sumy[o] + tv * sumx[o] + 1ll * len * sv * tv; sumxx[o] += 2 * sv * sumx[o] + 1ll * len * sv * sv; sumx[o] += 1ll * sv * len; sumy[o] += 1ll * tv * len; S[o] += sv; T[o] += tv; } else { sv += l; tv += l; sumxy[o] = 1ll * len * sv * tv + pre1(len - 1) * (sv + tv) + pre2(len - 1); sumxx[o] = 1ll * len * sv * sv + 2 * pre1(len - 1) * sv + pre2(len - 1); sumx[o] = 1ll * len * sv + pre1(len - 1); sumy[o] = 1ll * len * tv + pre1(len - 1); S[o] = sv - l; T[o] = tv - l; } } inline void push_down(int o, int l, int r) { if (tag[o]) { modify(lson, tag[o], S[o], T[o]); modify(rson, tag[o], S[o], T[o]); tag[o] = S[o] = T[o] = 0; } } inline void push_up(int o) { sumxy[o] = sumxy[ls] + sumxy[rs]; sumxx[o] = sumxx[ls] + sumxx[rs]; sumx[o] = sumx[ls] + sumx[rs]; sumy[o] = sumy[ls] + sumy[rs]; } void Update(int o, int l, int r, int opt, int ul, int ur, int sv, int tv) { if (ul <= l && r <= ur) { modify(o, l, r, opt, sv, tv); return; } push_down(o, l, r); if (ul <= mid) Update(lson, opt, ul, ur, sv, tv); if (ur > mid) Update(rson, opt, ul, ur, sv, tv); push_up(o); } ll Query(int o, int l, int r, int ql, int qr, ll *sum) { if (ql <= l && r <= qr) return sum[o]; push_down(o, l, r); ll res = 0; if (ql <= mid) res += Query(lson, ql, qr, sum); if (qr > mid) res += Query(rson, ql, qr, sum); push_up(o); return res; } void Build(int o, int l, int r, int *x, int *y) { if (l == r) { sumxy[o] = 1ll * x[l] * y[r]; sumxx[o] = 1ll * x[l] * x[r]; sumx[o] = x[l]; sumy[o] = y[r]; return; } Build(lson, x, y); Build(rson, x, y); push_up(o); } }; Segment_Tree<N << 2> T; int n, m, x[N], y[N]; #define root 1, 1, n int main () { File(); n = read(); m = read(); For (i, 1, n) x[i] = read(); For (i, 1, n) y[i] = read(); T.Build(root, x, y); while (m --) { int opt = read(), l = read(), r = read(); if (opt == 1) { ll sumxy = T.Query(root, l, r, T.sumxy), sumxx = T.Query(root, l, r, T.sumxx), sumx = T.Query(root, l, r, T.sumx), sumy = T.Query(root, l, r, T.sumy), len = r - l + 1; double averx = 1.0 * sumx / len, avery = 1.0 * sumy / len, k = 1.0 * (sumxy - len * averx * avery) / (sumxx - len * averx * averx); printf ("%.6lf\n", k); } else { int sv = read(), tv = read(); T.Update(root, opt, l, r, sv, tv); } } return 0; }