這套題實在是太神仙了。。作了我很久。。。好多題都是去搜題解纔會的 TAT。html
剩的那道題先咕着,若是省選沒有退役就來填吧。c++
丟 $Y$ 次骰子,骰子有 $X$ 面,每一面的機率均等,取值爲 $[0, X)$ ,問最後取值在 $[a, b]$ 之間的機率。git
一個浮點數,絕對偏差不超過 $0.013579$ 爲正確。github
每組數據有 $10$ 次詢問。數組
$100%$ 的數據,$T \leq 10$,$2 \leq X \leq 20$,$1 \leq Y \leq 200000$,$0 \leq A \leq B \leq (X − 1)Y$ ,保證知足 $Y > 800$ 的數據不超過 2 組。app
一開始不難想到一個暴力作法,設 $P(x)$ 爲丟一次骰子的機率生成函數,也就是 $\displaystyle P(x) = \sum_{i = 0}^{X - 1} \frac{1}{X} x^i$ ,咱們其實就是求 $P^Y(x)$ 的 $x^a \sim x^b$ 項的係數之和。函數
咱們考慮一開始用 $FFT$ 求出 $2^k \ge (X - 1) \times Y$ 個單位根的點值。優化
那麼多項式乘法就能夠變爲點值的乘法,也就是說每一個點值對應變成它的 $Y$ 次方,就能夠在 $\mathcal O(XY \log XY)$ 的時間內處理出這個多項式的 $Y$ 次方的答案啦。ui
這樣在考場應該只有 $60 \sim 70pts$ 可是因爲 $LOJ$ 機子神速,能夠直接過。。。代碼在這裏。this
至於正解,與這個前面提到那個暴力解法是徹底沒有關係的。。
若是你足夠聰明,看懂了出題人的提示,那就會作啦。
須要知道一個 中心極限定理。
如下內容來自維基百科:
中心極限定理說明,在適當的條件下,大量相互獨立隨機變量的均值經適當標準化後依分佈收斂於 正態分佈 。
設隨機變量 $X_1, X_2, \dots, X_n$ 獨立同分布,而且具備有限的 數學指望 和 方差 : $E(X_i)=μ, D(X_i)=\sigma^2 \not = 0(i=1,2,\dots, n)$。
記 $\displaystyle \bar{X} = \frac{1}{n} \sum_{i = 1}^{n} X_i, \zeta_n = \frac{\bar{x} - \mu}{\sigma / \sqrt n}$ ,則 $\displaystyle \lim_{n \to \infty} P(\zeta_n \le z) = \Phi (z)$ 。
其中 $\Phi (z)$ 是標準正態分佈函數。
若是你回去上過文化課,學過統計中的正態分佈應該就會啦。
說人話??好吧。。
$n$ 個獨立同分布,且具備有限的數學指望 $\mu$ 和方差 $\sigma ^2$ 的隨機變量,他們的平均值的分佈函數在 $n \to \infty$ 時近似爲位置參數爲 $\mu$ 尺度參數爲 $\sigma$ 的正態分佈。
至於標準正態分佈函數其實就是:
$$ \Phi(x) = {1 \over \sigma\sqrt{2\pi} }\exp(- {{(x-\mu )^2 \over 2\sigma^2}}) $$
也就是最後答案在 $[a, b]$ 的機率爲 $\displaystyle P(a \le X \le b) = \int_{a}^b \Phi(x) \mathrm{d}x$ 。
至於這個積分是個初等函數,而且極其光滑,那麼咱們利用辛普森就能夠積出來了。
可是有不少點值都趨近於 $0$ ,會被卡。
此時利用高中學的 $3\sigma$ 原則 (2017全國一卷理科數學) ,可知, $P(\mu - 3 \sigma < x \le \mu + 3 \sigma) \approx 0.9974$ 。
落在剩下區間的機率不足 $0.3 %$ 根據題目要求的精度基本能夠忽略。
那麼獲得了最後的解法,在 $Y$ 較小用 $FFT$ 或者暴力卷積實現,在 $Y$ 比較大的時候用辛普森求積分就好啦。
獨立同分布變量能夠利用正態分佈函數求積分快速計算估計值。
#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; using vd = vector<double>; 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 ("2267.in", "r", stdin); freopen ("2267.out", "w", stdout); #endif } const int N = 1610; struct Complex { double re, im; inline Complex friend operator + (const Complex &lhs, const Complex &rhs) { return (Complex) {lhs.re + rhs.re, lhs.im + rhs.im}; } inline Complex friend operator - (const Complex &lhs, const Complex &rhs) { return (Complex) {lhs.re - rhs.re, lhs.im - rhs.im}; } inline Complex friend operator * (const Complex &lhs, const Complex &rhs) { return (Complex) {lhs.re * rhs.re - lhs.im * rhs.im, lhs.re * rhs.im + lhs.im * rhs.re}; } }; const double Pi = acos(-1.0), eps = 1e-15; namespace poly { const int Maxn = 1 << 24; int len, rev[Maxn]; void FFT(Complex *P, int opt) { Rep (i, len) if (i < rev[i]) swap(P[i], P[rev[i]]); for (int i = 2, p = 1; i <= len; p = i, i <<= 1) { Complex Wi = (Complex) {cos(2 * Pi / i), opt * sin(2 * Pi / i)}; for (int j = 0; j < len; j += i) { Complex x = (Complex) {1, 0}; for (int k = 0; k < p; ++ k, x = x * Wi) { Complex u = P[j + k], v = x * P[j + k + p]; P[j + k] = u + v; P[j + k + p] = u - v; } } } if (!~opt) Rep (i, len) P[i].re /= len; } Complex A[Maxn], B[Maxn], C[Maxn]; inline vd operator * (const vd &a, const vd &b) { int na = a.size() - 1, nb = b.size() - 1, nc = na + nb, cnt = 0; for (len = 1; len <= nc; len <<= 1) ++ cnt; Rep (i, len) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1)); Rep (i, len) A[i] = (Complex) {i <= na ? a[i] : 0, 0}; Rep (i, len) B[i] = (Complex) {i <= nb ? b[i] : 0, 0}; FFT(A, 1); FFT(B, 1); Rep (i, len) C[i] = A[i] * B[i]; FFT(C, -1); vd res(nc + 1); For (i, 0, nc) res[i] = C[i].re; return res; } } template<typename T> inline T fpm(T x, int power) { T res = x; -- power; for (; power; power >>= 1, x = x * x) if (power & 1) res = res * x; return res; } double mu, sigma; #define sqr(x) ((x) * (x)) double f(double x) { return exp(- sqr(x - mu) / (2 * sqr(sigma))) / (sqrt(2 * Pi) * sigma); } double Asr(double l, double r) { return (r - l) * (f(l) + 4 * f((l + r) / 2) + f(r)) / 6; } double Simpson(double l, double r, double area) { double mid = (l + r) / 2, la = Asr(l, mid), ra = Asr(mid, r); if (fabs(area - la - ra) < eps) return la + ra; return Simpson(l, mid, la) + Simpson(mid, r, ra); } int main () { File(); int cases = read(); while (cases --) { int x = read(), y = read(); using namespace poly; if (y <= 2000) { int nc = (x - 1) * y, cnt = 0; for (len = 1; len <= nc; len <<= 1) ++ cnt; Rep (i, len) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1)); Rep (i, len) A[i] = (Complex) {i < x ? 1.0 / x : 0, 0}; FFT(A, 1); Rep (i, len) A[i] = fpm(A[i], y); FFT(A, -1); vd ans(nc + 1); Rep (i, ans.size()) { ans[i] = A[i].re; if (i) ans[i] += ans[i - 1]; } For (i, 1, 10) { int l = read(), r = read(); printf ("%.6lf\n", ans[r] - (l ? ans[l - 1] : 0)); } } else { mu = (x - 1) / 2.0 * y; sigma = sqrt(y * (1.0 * x * x - 1) / 12.0); For (i, 1, 10) { double l = max((double)read(), mu - 3 * sigma); double r = min((double)read(), mu + 3 * sigma); double ans = l <= r ? Simpson(l, r, Asr(l, r)) : 0; printf ("%.6lf\n", ans); } } } return 0; }
有 $n$ 個點的一顆樹,每一個點有 $a_i$ 個物品,權值爲 $v_i$ ,能夠取走 $t$ 個物品,取一個物品的時候它的父親至少也要取一個物品。
假設取了物品的節點最大深度爲 $h$ 要求 $t - h \le k$ 。
最大化最後取物品的權值和。
$Q$ 爲數據組數。
$1 \leq Q \leq 5$,$1 \leq n \leq 20,000$,$1 \leq k \leq 500,000$,$1 \leq nk \leq 25,000,000$,$1 \leq a_i \leq 10^8$,$1 \leq v_i \le 100$
首先忽略 $t$ 的要求,就是 樹上依賴多重揹包 。
這個怎麼作呢?樹上依賴揹包其實就是你考慮按樹的後序遍歷(先兒子後根)標號。
令 $f_{i, j}$ 爲後序遍歷前 $i$ 個節點,揹包大小爲 $j$ 的時候的最大權值。
而後依次轉移每一個點,若是這個點要選的話,那麼它能夠直接從 $f_{i - 1, j - v} + w$ 轉移過來,不然它不選就只能從 $f_{i - sz, j}$ 轉移過來(也就是子樹內一個都不能選)。
而後因爲是多重揹包,二進制分組被卡了,只能單調隊列優化,物品大小爲 $1$ 的話就不須要按模數分類了。具體來講,對於 $f_{i - 1, j - kv} + kw (v = 1)$ 能夠拆成 $(f_{i - 1, j - k} - (j - k) w) + jw$ 的形式。前者只與 $j - k$ 有關,而後是要求 $[j - c, j]$ 這個的最大值,這很顯然是能夠用單調隊列的。
最後考慮 $t - h \le k$ 的限制是什麼意思,其實就是根到一個節點路徑上每一個點均可以避免費拿一個,剩下的拿 $k$ 個。顯然咱們是選一個葉子節點是最優的,但注意不是選最深的那個葉子!
而後咱們只須要考慮,這條路上都少一個容量的答案如何快速算,考慮拆點,把 $i$ 號點拆個容量爲 $a_i - 1$ 價值爲 $v_i$ 的節點 $i'$ 出來,把 $i \to i'$ 連一條邊。
此時原來樹上每一個點的容量就剛好是 $1$ 了,新加的咱們能夠在外面算。
而後不難發現除了這條鏈的子樹就是對應着後序遍歷上的兩段區間,咱們正反各作一遍 $dp$ 就能求出來了。
最後複雜度是 $\mathcal O(nk)$ 的。
經典揹包仍是要多記一下。圖論仍是要多想下拆點,有可能就好作許多。
#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 ("2268.in", "r", stdin); freopen ("2268.out", "w", stdout); #endif } const int N = 40100, K = 500100, M = 5.5e7 + 10; int n, m; int dfn[2][N], lis[2][N], sz[N], clk; vector<int> G[N]; int w[N], c[N], sum[N]; void Dfs_Init(int u, int id) { sz[u] = 1; sum[u] += w[u]; for (int v : G[u]) sum[v] = sum[u], Dfs_Init(v, id), sz[u] += sz[v]; lis[id][dfn[id][u] = ++ clk] = u; } #define dp(id, x, y) dp[id][(x) * (m + 1) + (y)] int dp[2][M], val[K], que[K], fr, tl; void Dp(int id) { For (i, 1, clk) { int u = lis[id][i]; For (j, 0, m) dp(id, i, j) = dp(id, i - sz[u], j); fr = 1; tl = 0; For (j, 0, m) { while (fr <= tl && j - que[fr] > c[u]) ++ fr; if (fr <= tl) chkmax(dp(id, i, j), val[que[fr]] + j * w[u]); val[j] = dp(id, i - 1, j) - j * w[u]; while (fr <= tl && val[que[tl]] <= val[j]) -- tl; que[++ tl] = j; } } } bool leaf[N]; int tot; void Init() { Set(leaf, true); For (i, 1, n) G[i].clear(); } int main () { File(); int cases = read(); while (cases --) { Init(); tot = n = read(), m = read(); int rt = 0; For (i, 1, n) { int fa = read(); if (fa) { leaf[fa] = false; G[fa].push_back(i); } else rt = i; int ai = read(), vi = read(); w[i] = vi; c[i] = 1; if (ai > 1) { int id = ++ tot; w[id] = vi; c[id] = ai - 1; G[i].push_back(id); } } Rep (id, 2) { sum[rt] = clk = 0, Dfs_Init(rt, id), Dp(id); For (i, 1, n) reverse(G[i].begin(), G[i].end()); For (i, 1, n) For (j, 1, m) chkmax(dp(id, i, j), dp(id, i, j - 1)); } int ans = 0; For (i, 1, n) if (leaf[i]) { For (j, 0, m) chkmax(ans, dp(0, dfn[0][i] - 1, j) + dp(1, dfn[1][i] - sz[i], m - j) + sum[i]); } For (i, 1, tot) For (j, 0, m) dp(0, i, j) = dp(1, i, j) = 0; printf ("%d\n", ans); } return 0; }
一棵有 $n$ 個點的樹 $T$ ,節點帶權,兩種操做:
$\text{Change x y}$ : 將編號爲 $x$ 的結點的權值修改成 $y$ 。
$\text{Query k}$ : 詢問有多少棵 $T$ 的非空連通子樹,知足其中全部點權值的異或和剛好爲 $k$ 。
$n, q \le 3 \times 10^4, m \le 128$ 修改操做不超過 $10^4$ 。
動態 $dp$ 經典題目,參考了 dy0607 的博客 orz dy。
先考慮暴力怎麼作,設 $f [ i ] [ j ]$ 表示包含 $i$ 的連通塊( $i$ 爲連通塊中深度最小的節點),異或和爲 $j$ 的方案數,從下往上DP便可。
注意到在合併子樹信息時其實是在作異或卷積,能夠用 $FWT$ 優化,注意 $FWT$ 不存在循環卷積的性質十分優秀,加減乘除全均可以在點值上作。那麼能夠全程用 $FWT$ 以後的點值計算,算答案時再 $IFWT$ 回去,此時的複雜度爲 $\mathcal O (nqm + qm \log m)$ 。
爲了動態修改,咱們利用重鏈剖分變成序列問題,設 $i$ 重兒子爲 $j$ ,輕兒子集合爲 $child(i)$ 。
利用集合冪級數的形式來定義 $dp$ 轉移:
$$ F_i(z) = \sum_{j = 0}^{m - 1} f[i][j] z^j = F_j(z) \times z^{v_i} \times (\prod_{c \in child(i)} F_c(z)) + z^0 $$
注意 $z_0$ 對應着轉移時候的空樹,統計答案的時候應該去掉。咱們用另一個冪級數 $G$ 統計答案:
$$ G_i(z) = G_j(z) + (\sum_{c \in child(i)} G_c(z)) + (F_i(z) - z^0) $$
咱們假設把輕兒子的冪級數設爲常數,即:
$$ \begin{aligned} Gl_i(z) &= \sum_{c \in child(i)} G_c(z)\ Fl_i(z) &= z^{v_i} \times \prod_{c \in child(i)} F_c(z) \end{aligned} $$
\begin{pmatrix} F_i & G_i & 1 \end{pmatrix} %]]> $$
注意此處的 $1$ 對應的就是集合冪級數的單位元 $z_0$ 。
咱們考慮用一個線段樹維護矩陣的乘積,那麼咱們就能夠動態算出重鏈頂端的 $F, G$ 了。
那麼每次修改的時候咱們只須要考慮那些常數要怎麼改了。
對於 $Gl_i(z)$ 比較好改,假設當前修改的是 $c \in child(i)$ 。那麼只須要減掉以前的 $G(c)$ 加上後來的 $G'(c)$ 。
可是對於 $Fl_i(z)$ 就很差改了,須要變換的其實就是 $F_c(z)$ 這個先要除掉,除的時候可能會除 $0$ ,看到網上有神仙作法是維護 $0$ 的個數,我不太會。。
其實能夠對於每一個點在線段樹上維護它輕兒子的 $F_c(z)$ 的值,那麼咱們能夠直接在線段樹上修改而後求出 $Fl_i(z)$ 的值。
其中有一個常數優化是,矩陣上 $0$ 的跳過去,這個優化十分顯著。
其實只須要維護 $4$ 個值 $a, b, c, d$ ,以下圖。
$$\begin{pmatrix} \underline{a_1} & \underline{b_1} & 0 \ 0 & 1 & 0 \ \underline{c_1} & \underline{d_1} & 1 \end{pmatrix} \times \begin{pmatrix} \underline{a_2} & \underline{b_2} & 0 \ 0 & 1 & 0 \ \underline{c_2} & \underline{d_2} & 1 \end{pmatrix} = \begin{pmatrix} \underline{a_1 a_2} & \underline{b_1 + a_1 b_2} & 0 \ 0 & 1 & 0 \ \underline{a_2 c_1 + c_2} & \underline{b_2 c_1 + d_1 + d_2} & 1 \end{pmatrix}$$
複雜度是 $\mathcal O(qm(\log^2 n + \log m))$ 。
$ddp$ 主要就是考慮輕兒子如何貢獻的,把這個當作常數遞推的係數。每次修改的時候只須要修改輕兒子的貢獻上來的矩陣就好了。
#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 #define plus Plus 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 ("2269.in", "r", stdin); freopen ("2269.out", "w", stdout); #endif } const int N = 30100, M = 128, Mod = 10007, inv2 = (Mod + 1) / 2; namespace Computation { inline void add(int &a, int b) { if ((a += b) >= Mod) a -= Mod; } inline int plus(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } inline int dec(int a, int b) { return (a -= b) < 0 ? a + Mod : a; } inline int mul(int a, int b) { return 1ll * a * b % Mod; } } using namespace Computation; int n, m, q, v[N]; struct Poly { int x[M]; inline void clear() { Set(x, 0); } Poly(int id = 0) { clear(); if (id == 1) Rep (i, m) x[i] = 1; } inline Poly operator = (const Poly &rhs) { Rep (i, m) x[i] = rhs.x[i]; return *this; } inline Poly friend operator + (const Poly &lhs, const Poly &rhs) { Poly res; Rep (i, m) res.x[i] = plus(lhs.x[i], rhs.x[i]); return res; } inline Poly friend operator - (const Poly &lhs, const Poly &rhs) { Poly res; Rep (i, m) res.x[i] = dec(lhs.x[i], rhs.x[i]); return res; } inline Poly friend operator * (const Poly &lhs, const Poly &rhs) { Poly res; Rep (i, m) res.x[i] = mul(lhs.x[i], rhs.x[i]); return res; } void FWT(int opt) { for (int i = 2, p = 1; i <= m; p = i, i <<= 1) for (int j = 0; j < m; j += i) Rep (k, p) { int u = x[j + k], v = x[j + k + p]; x[j + k] = mul(plus(u, v), opt == 1 ? 1 : inv2); x[j + k + p] = mul(dec(u, v), opt == 1 ? 1 : inv2); } } inline void Out() { this -> FWT(-1); Rep (i, m) printf ("%d%c", x[i], i == m - 1 ? '\n' : ' '); this -> FWT(1); } }; struct Info { Poly a, b, c, d; inline Info friend operator * (const Info &lhs, const Info &rhs) { return (Info) { lhs.a * rhs.a, lhs.a * rhs.b + lhs.b, rhs.a * lhs.c + rhs.c, rhs.b * lhs.c + lhs.d + rhs.d}; } inline void Out() { puts("-----------"); a.Out(); b.Out(); c.Out(); d.Out(); puts("-----------"); } }; #define mid ((l + r) >> 1) #define lson o << 1, l, mid #define rson o << 1 | 1, mid + 1, r template<typename T, int maxn> struct Segment_Tree { T mulv[maxn]; void build(int o, int l, int r, T *tmp) { if (l == r) { mulv[o] = tmp[mid]; return; } build(lson, tmp); build(rson, tmp); mulv[o] = mulv[o << 1 | 1] * mulv[o << 1]; } void update(int o, int l, int r, int up, T uv) { if (l == r) { mulv[o] = uv; return; } up <= mid ? update(lson, up, uv) : update(rson, up, uv); mulv[o] = mulv[o << 1 | 1] * mulv[o << 1]; } T query(int o, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) return mulv[o]; if (qr <= mid) return query(lson, ql, qr); if (ql > mid) return query(rson, ql, qr); return query(rson, ql, qr) * query(lson, ql, qr); } }; Segment_Tree<Info, N << 2> mat; Segment_Tree<Poly, N << 2> FL; #define root 1, 1, n vector<int> E[N]; int son[N], sz[N], fa[N]; void Dfs_Init(int u) { sz[u] = 1; for (int v : E[u]) if (v != fa[u]) { fa[v] = u, Dfs_Init(v); sz[u] += sz[v]; if (sz[v] > sz[son[u]]) son[u] = v; } } int top[N], dfn[N], id[N], enl[N]; void Dfs_Part(int u) { static int clk = 0; id[dfn[u] = ++ clk] = enl[u] = u; top[u] = son[fa[u]] == u ? top[fa[u]] : u; if (son[u]) Dfs_Part(son[u]), enl[u] = enl[son[u]]; for (int v : E[u]) if (v != son[u] && v != fa[u]) Dfs_Part(v); } Poly F[N], G[N], Gl[N], Fl[N], base[N]; int Beg[N], End[N], fid[N], tot = 0; void Dp(int u) { F[u] = base[v[u]]; G[u].clear(); for (int v : E[u]) if (v != fa[u] && v != son[u]) { Dp(v); F[u] = F[u] * F[v]; G[u] = G[u] + G[v]; } Gl[u] = G[u]; Fl[u] = F[u]; if (son[u]) { Dp(son[u]); F[u] = F[u] * F[son[u]]; G[u] = G[u] + G[son[u]]; } G[u] = G[u] + F[u]; F[u] = F[u] + 1; for (int v : E[u]) if (v != son[u] && v != fa[u]) if (!fid[v]) { End[u] = fid[v] = ++ tot; if (!Beg[u]) Beg[u] = fid[v]; } } void ask(int o) { Info cur = mat.query(root, dfn[o], dfn[enl[o]]); F[o] = (Fl[enl[o]] + 1) * cur.a + cur.c; G[o] = (Fl[enl[o]] + 1) * cur.b + base[v[enl[o]]] + cur.d; } void get_mat(int o) { Fl[o] = Beg[o] ? FL.query(root, Beg[o], End[o]) * base[v[o]] : base[v[o]]; mat.update(root, dfn[o], son[o] ? (Info) {Fl[o], Fl[o], 1, Gl[o]} : (Info) {1, 0, 0, 0}); } Info I[N]; Poly P[N]; int main () { File(); n = read(); m = read(); For (i, 1, n) v[i] = read(); For (i, 1, n - 1) { int u = read(), v = read(); E[u].push_back(v); E[v].push_back(u); } Rep (i, m) base[i].x[i] = 1, base[i].FWT(1); Dfs_Init(1); Dfs_Part(1); Dp(1); For (u, 1, n) { I[dfn[u]] = son[u] ? (Info) {Fl[u], Fl[u], 1, Gl[u]} : (Info) {1, 0, 0, 0}; if (fid[u]) P[fid[u]] = F[u]; } mat.build(root, I); FL.build(root, P); q = read(); while (q --) { static char str[10]; scanf ("%s", str + 1); if (str[1] == 'C') { int x = read(), y = read(); v[x] = y; for (; x; x = fa[x]) { if (fa[top[x]]) Gl[fa[top[x]]] = Gl[fa[top[x]]] - G[top[x]]; get_mat(x); if (x == 1) break; x = top[x]; ask(x = top[x]); if (fid[x]) FL.update(root, fid[x], F[x]); Gl[fa[x]] = Gl[fa[x]] + G[x]; } } else { ask(1); Poly ans = G[1]; ans.FWT(-1); printf ("%d\n", ans.x[read()]); } } return 0; }
這道題題意看了我很久。。。
給你一個 $n$ 個點 $m$ 條邊的有向圖,同時給你一個 $k$ 個節點的字典樹。
對於每條邊有兩個屬性,一個是它的邊權 $c_i$ 另一個是這條邊會對應到字典樹上一個節點 $d_i$ 。
而後你從 $1$ 開始走,一開始手上有個字符串 $S$ 爲空串,每次走一條邊 $i$ 須要花費的代價是 $c_i$ 加上 $d_i$ 對應的字符串與 $S$ 的 $LCP$ 長度,而後 $S$ 變成 $d_i$ 對應的字符串。
問從 $1$ 號店開始到 $2 \sim n$ 全部點的最短路的長度。
對於 $100%$ 的數據,$T \leq 10$,$2 \leq n \leq 50000$,$1 \leq m \leq 50000$,$1 \leq k \leq 20000$,保證知足 $n > 5000$ 或 $m > 5000$ 的數據不超過 $2$ 組。
顯然暴力考慮的話,咱們確定要記過來的前一條邊是什麼,那麼就是 $\mathcal O(m^2)$ 的,不夠優秀。
發現從點到點時的代價是不肯定的,而從邊到邊的代價是必定的,因此將邊轉化爲點,點權 $v_i$ 爲原圖中的邊權 $c_i$ ,而後以 $1$ 爲起點的邊距離 $dis_i = v_i$ 而後跑完最短路後的每一個點 $x$ 的距離,其實就是全部指向 $x$ 的邊的 $\min_{b_i = x} {dis_i}$ 。
接下來咱們只須要考慮考慮如何建邊,新圖中點 $x$ 到點 $y$ 的舉例其實就是 $d_x$ 與 $d_y$ 在字典樹上 $lca$ 的深度。
那對原圖中每一個點考慮它相鄰的全部邊能夠一塊兒考慮建邊,把這些邊按 $d_i$ 在字典樹上的 $dfs$ 序從小到大排序。
設 $len_x = lcp(d_x, d_{x + 1})$ 那麼有 $\displaystyle lcp(d_x, d_y) = \min_{i = x}^{y - 1} len_i$ 。這個能夠考慮這些節點在字典樹上的虛樹,兩個節點對應的 $lca$ 就是被夾在其中的相鄰點對最淺的那個 $lca$ 。
咱們就能夠考慮枚舉分界點 $x$ ,對於全部標號 $\le x$ 的點與全部標號 $\ge x$ 的點的距離不會超過 $len$ ,那麼咱們建前綴和後綴輔助點就好了。
可是注意入邊和出邊不能同時連一個前綴或者後綴,否則會直接「短路」。咱們能夠考慮搞兩個前綴和後綴,一個入邊連前綴出邊連後綴,另一個出邊連前綴入邊連後綴。
爲何要這樣呢?由於入邊和出邊的 $dfs$ 序的大小關係有兩種,要分開討論。
說的好像有點玄乎,掛張圖。以下是樣例關於點 $2$ 建出來的圖:
<center> <img src="http://images.cnblogs.com/cnblogs_com/zjp-shadow/1056673/o_rebuild.png" width="60%" height="60%"> </center>
點數和邊數都是 $\mathcal O(m)$ 的,最後跑一次 $Dijkstra$ 是 $O(m \log m)$ 的。
最短路的題通常都是考慮重構圖,下降邊數/點數。
若是是取 $\min_{i = l}^{r} a_i$ 用雙層圖跑最短路,若是是 $\sum_{i = l}^{r} a_i$ 就差分跑最短路。
#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 #define pb push_back 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 ("2270.in", "r", stdin); freopen ("2270.out", "w", stdout); #endif } const int N = 1e6 + 1e3, M = N << 2; int n, m, k, val[N], id[N], tot; vector<int> In[N], Out[N], ch[N]; int Head[N], Next[M], to[M], weight[M], e; inline void add_edge(int u, int v, int w, bool rev = false) { if (rev) swap(u, v); to[++ e] = v; Next[e] = Head[u]; Head[u] = e; weight[e] = w; } int clk, dfn[N], anc[N][20], Log2[N], dep[N]; void Dfs_Init(int u) { dfn[u] = ++ clk; for (int v : ch[u]) dep[v] = dep[u] + 1, Dfs_Init(v), anc[v][0] = u; } inline int Lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); int gap = dep[x] - dep[y]; Fordown (i, Log2[gap], 0) if (gap >> i & 1) x = anc[x][i]; if (x == y) return x; Fordown (i, Log2[dep[x]], 0) if (anc[x][i] != anc[y][i]) x = anc[x][i], y = anc[y][i]; return anc[x][0]; } void Rebuild() { static int pre[N][2], suf[N][2]; For (i, 1, n) { struct Node { int type, num, pos; }; vector<Node> V; for (int x : In[i]) V.push_back({0, x, id[x]}); for (int x : Out[i]) V.push_back({1, x, id[x]}); sort(V.begin(), V.end(), [&](Node a, Node b) { return dfn[a.pos] < dfn[b.pos]; }); Rep (i, V.size()) Rep (id, 2) { pre[i][id] = ++ tot; if (V[i].type == id) add_edge(V[i].num, pre[i][id], 0, V[i].type); if (i) add_edge(pre[i - 1][id], pre[i][id], 0, id); } Fordown (i, V.size() - 1, 0) Rep (id, 2) { suf[i][id] = ++ tot; if (V[i].type == (id ^ 1)) add_edge(V[i].num, suf[i][id], 0, V[i].type); if (i < int(V.size()) - 1) add_edge(suf[i][id], suf[i + 1][id], 0, id); } Rep (i, V.size()) Rep (id, 2) add_edge(pre[i][id], suf[i][id], dep[V[i].pos], id); Rep (i, V.size() - 1) { int dis = dep[Lca(V[i].pos, V[i + 1].pos)]; Rep (id, 2) add_edge(pre[i][id], suf[i + 1][id], dis, id); } } } int dis[N]; bool vis[N]; void Dijkstra() { priority_queue<pair<int, int>> P; Set(dis, 0x7f); Set(vis, false); for (int v : Out[1]) P.emplace(- (dis[v] = val[v]), v); while (!P.empty()) { int u = P.top().second; P.pop(); if (vis[u]) continue; vis[u] = true; for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) if (chkmin(dis[v], dis[u] + weight[i] + val[v])) P.emplace(- dis[v], v); } } int main () { File(); int cases = read(); while (cases --) { n = read(); tot = m = read(); k = read(); Set(val, 0); Set(Head, 0); e = clk = 0; For (i, 1, n) In[i].clear(), Out[i].clear(); For (i, 1, k) ch[i].clear(); For (i, 1, m) { Out[read()].pb(i); In[read()].pb(i); val[i] = read(), id[i] = read(); } For (i, 1, k - 1) { int u = read(), v = read(); read(); ch[u].pb(v); } Dfs_Init(1); For (i, 2, k) Log2[i] = Log2[i >> 1] + 1; For (j, 1, Log2[k]) For (i, 1, k) anc[i][j] = anc[anc[i][j - 1]][j - 1]; Rebuild(); Dijkstra(); For (i, 2, n) { int ans = 0x7f7f7f7f; for (int v : In[i]) chkmin(ans, dis[v]); printf ("%d\n", ans); } } return 0; }
給你一個長度爲 $n$ 的數組 $f(i)$ ,你須要構造一個集合,知足對於全部 $i$ 能被集合中元素湊出來的方案(只考慮出現次數,不考慮順序)對於 $p$ 取模爲 $f(i)$ ( $p$ 爲質數)。
而後解要字典序儘可能小。
對於 $100%$ 的數據,有 $1 \leq n < 2^{18}$,$10^6 \leq p < 2^{30}$,$\forall i, 0 \leq f(i)< p$ 。
對於計數類揹包咱們一般考慮生成函數,令 $a_i \in {0, 1}$ 表示 $i$ 是否出如今集合中,那麼 $f$ 對應的生成函數就是:
$$ F(x) = \prod_{i = 1}^{n} (\frac{1}{1 - x^i})^{a_i} $$
如今就變成了構造一組 $a_i$ 知足 $F(x)$ 。
兩邊取對數那麼有:
$$ -\ln F(x) = \sum_{i = 1} ^ n a_i \ln(1 - x ^ i) $$
若是作過各類揹包套路題的就知道:
$$ \ln(1 - x ^ i) = -\sum_{j = 1}^{\infty} \frac{x ^ {ij}}{j} $$
這個證實能夠看 zsy 大佬的博客 。
考慮代入前面的式子,就有
$$ -\ln F(x) = -\sum_{i = 1}^n a_i \sum_{j = 1}^{\infty} \frac{x ^ {ij}}{j} $$
令 $T = ij$ 交換和式,那麼就有
$$ \ln F(x) = \sum_{T = 1}^{\infty}x ^ T \sum_{d|T}a_d \times \frac dT $$
那麼咱們就只要求出 $\ln F(x)$ ,而後就能夠獲得 $\sum_{d | T}a_d \times \frac dT$ 。
也就是 $ia_i = \sum_{k | i} k c_k$ ,其中 $c_k$ 爲給定的係數。
能夠莫比烏斯反演,可是沒有必要。咱們從前日後枚舉每一個 $d$ 咱們把每一個 $d$ 的倍數 $T$ 減掉當前的值就好了。
多項式求 $\ln$ 複雜度在於多項式除法,用主定理證的 $\mathcal O(n \log n)$ 實際上常數大到飛起(還有個 $MTT$ 。。)
牢記普通生成函數的形式。
$$ \frac{1}{1 - x^i} = 1 + x^i + x^{2i} + \cdots $$
以及一個經典多項式對數的形式。
$$ \ln (1 - A(x)) = - \sum_{i \ge 1} \frac{A^i(x)}{i} $$
傻吊出題人出個求 $\ln$ 的題還要任意模數 $FFT$ 。。
注意用 $MTT$ 的話要預處理單位根來卡精度。
#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 ("2271.in", "r", stdin); freopen ("2271.out", "w", stdout); #endif } const double Pi = acos(-1.0); struct Complex { double re, im; }; inline Complex operator + (const Complex &lhs, const Complex &rhs) { return (Complex) {lhs.re + rhs.re, lhs.im + rhs.im}; } inline Complex operator - (const Complex &lhs, const Complex &rhs) { return (Complex) {lhs.re - rhs.re, lhs.im - rhs.im}; } inline Complex operator * (const Complex &lhs, const Complex &rhs) { return (Complex){lhs.re * rhs.re - lhs.im * rhs.im, lhs.re * rhs.im + lhs.im * rhs.re}; } const int Maxn = (1 << 22) + 5; int len, rev[Maxn]; Complex W[Maxn]; void FFT(Complex *P, int opt) { For (i, 0, len - 1) if (i < rev[i]) swap(P[i], P[rev[i]]); for (int i = 2, p = 1; i <= len; p = i, i <<= 1) { Rep (k, p) W[k] = (Complex){cos(2 * Pi * k / i), opt * sin(2 * Pi * k / i)}; for (int j = 0; j < len; j += i) { For (k, 0, p - 1) { Complex u = P[j + k], v = P[j + k + p] * W[k]; P[j + k] = u + v; P[j + k + p] = u - v; } } } if (!~opt) For (i, 0, len - 1) P[i].re /= len; } void FFT_Init(int n) { int cnt = 0; for (len = 1; len <= n; len <<= 1) ++ cnt; For (i, 0, len - 1) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1)); } void Trans(int *a, Complex *P) { Rep (i, len) P[i] = (Complex){(double)a[i], 0}; FFT(P, 1); } const int Pow = (1 << 15) - 1; int F[Maxn], G[Maxn], CoefF[Maxn], CoefG[Maxn], res[Maxn], Mod; Complex A[Maxn], B[Maxn], C[Maxn], D[Maxn], sum[Maxn]; inline int Get_Mod(double x) { return (int)((x - floor(x / Mod) * Mod) + .5); } void Mult(Complex *a, Complex *b, int base, int opt = 1) { Rep (i, len) sum[i] = sum[i] + a[i] * b[i]; if (opt) { FFT(sum, -1); Rep (i, len) { res[i] = (res[i] + 1ll * base * Get_Mod(sum[i].re)) % Mod; sum[i] = (Complex){0, 0}; } } } void Mult(int *f, int *g, int *ans, int len) { FFT_Init(len); Rep (i, len << 1) res[i] = 0; Rep (i, len) CoefF[i] = f[i] & Pow, F[i] = f[i] >> 15; Rep (i, len) CoefG[i] = g[i] & Pow, G[i] = g[i] >> 15; Trans(CoefF, A); Trans(F, B); Trans(CoefG, C); Trans(G, D); Mult(A, C, 1); Mult(B, D, 1 << 30); Mult(A, D, 1 << 15, 0); Mult(B, C, 1 << 15); Rep (i, len << 1) ans[i] = res[i]; } inline int fpm(int x, int power) { int res = 1; for (; power; power >>= 1, x = 1ll * x * x % Mod) if (power & 1) res = 1ll * res * x % Mod; return res; } const int N = 1e6 + 1e3; int tmp[N]; void Get_Inv(int *a, int *b, int len) { if (len == 1) { b[0] = fpm(a[0], Mod - 2); return; } Get_Inv(a, b, len >> 1); Mult(a, b, tmp, len); Rep (i, len) tmp[i] = Mod - tmp[i]; tmp[0] += 2; Mult(tmp, b, b, len); } int der[N], inv[N]; void Get_Ln(int *a, int *b, int len) { For (i, 1, len - 1) der[i - 1] = 1ll * i * a[i] % Mod; Get_Inv(a, inv, len); Mult(der, inv, b, len); Fordown (i, len - 1, 0) b[i] = 1ll * b[i - 1] * fpm(i, Mod - 2) % Mod; } int x[N], f[N], ans[N]; int main () { File(); int n = read(); Mod = read(); f[0] = 1; For (i, 1, n) f[i] = read(); int len = 1; while (len <= n) len <<= 1; Get_Ln(f, f, len); For (i, 1, n) f[i] = 1ll * f[i] * i % Mod; For (i, 1, n) for (int j = i << 1; j <= n; j += i) (f[j] += Mod - f[i]) %= Mod; vector<int> ans; For (i, 1, n) if (f[i]) ans.push_back(i); printf ("%d\n", int(ans.size())); Rep (i, ans.size()) printf ("%d%c", ans[i], i == iend - 1 ? '\n' : ' '); return 0; }
給你字符串 $S$ 和 $T$ ,問你是否能把 $T$ 分紅三段重組後變成 $S$ ,若是可行則給出方案。
$T \le 30, 3 \leq n \leq 1000000$,$1 \leq S_i. T_i \leq m \leq 1000000$
能夠考慮把 $T$ 劃分紅 $3$ 段爲 $ABC$ 考慮重組後的形式,共有 $3! = 6$ 種方式。
省選沒退役就來填坑。