来补个档。
先离散化。对每个上升子序列计算权值是困难的,我们考虑每个位置对答案的贡献。
即我们想要知道对于每个 \(a_p\),\(i_k\) 最远能到哪里,使得存在一个 \(x \in (i_k, n]\) 满足 \(a_x > a_i\)。容易发现,若设 \(r_p\) 为最右端的满足 \(a_{r_p} > a_p\) 的下标,那么 \(i_k < r_p\)。
于是对于每一个 \(p\) 我们需要计算 “经过 \(p\),且最后一个元素在 \(r_p\) 之前的” 上升子序列个数,这可以容斥成 “经过 \(p\),且最后一个元素在 \(r_p\) 或 \(r_p\) 之后” 的上升子序列个数。但由于 \(r_p\) 的定义,对于任意 \(i \in (r_p, n]\) 都有 \(a_i \leq a_p\),这意味着这些位置不可能成为上升子序列的结尾元素,于是我们只需要计算 “经过 \(p\),且最后一个元素位置为 \(r_p\)” 的上升子序列个数。
接下来考虑如何计数。显然考虑把子序列拆成 \(p\) 之前和 \(p\) 到 \(r_p\) 两部分,前面是好算的。\(p\) 之后的部分我们考虑倒着处理,由于 \(r_p\) 是最靠右的满足条件的下标,所以任意一个从 \(p\) 开始的子序列结尾一定不超过 \(r_p\),只需要维护 “从 \(p\) 开始以最远位置结尾” 的子序列的方案数即可。时间复杂度 \(O(n \log n)\)。
/* 最黯淡的一个 梦最为炽热 万千孤单焰火 让这虚构灵魂鲜活 至少在这一刻 热爱不问为何 存在为将心声响彻 */ #include <bits/stdc++.h> #define pii pair<int, int> #define mp(x, y) make_pair(x, y) #define pb push_back #define eb emplace_back #define fi first #define se second #define int long long #define mem(x, v, n) memset(x, v, sizeof(int) * (n)) #define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n)) #define lob lower_bound #define upb upper_bound using namespace std; inline int read() { int x = 0, w = 1;char ch = getchar(); while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); } while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * w; } inline int min(int x, int y) { return x < y ? x : y; } inline int max(int x, int y) { return x > y ? x : y; } const int MN = 2e5 + 5; const int Mod = 1e9 + 7; inline int qPow(int a, int b = Mod - 2, int ret = 1) { while (b) { if (b & 1) ret = ret * a % Mod; a = a * a % Mod, b >>= 1; } return ret; } // #define dbg int N, M, a[MN], pre[MN], suf[MN], ans; vector <int> vr; inline void add(int &x, int y) { x += y; if (x >= Mod) x -= Mod; } inline void dec(int &x, int y) { x -= y; if (x < 0) x += Mod; } int c[MN], mx[MN], mxv[MN]; inline void Add1(int x, int v) { for (; x <= N; x += x & -x) add(c[x], v); } inline int Qry1(int x) { int res = 0; for(; x; x -= x & -x) add(res, c[x]); return res; } inline void Add2(int x, pii v) { for (; x; x -= x & -x) { if (mx[x] < v.fi) mx[x] = v.fi, mxv[x] = 0; if (mx[x] == v.fi) add(mxv[x], v.se); } } inline pii Qry2(int x) { int Mx = 0, Mxv = 0; for (; x <= N; x += x & -x) { if (Mx < mx[x]) Mx = mx[x], Mxv = 0; if (Mx == mx[x]) add(Mxv, mxv[x]); } return mp(Mx, Mxv); } inline void Work() { N = read(); vr.clear(), ans = 0; for (int i = 1; i <= N; i++) a[i] = read(), vr.pb(a[i]); for (int i = 1; i <= N; i++) c[i] = mx[i] = mxv[i] = 0; sort(vr.begin(), vr.end()); vr.erase(unique(vr.begin(), vr.end()), vr.end()); for (int i = 1; i <= N; i++) a[i] = lob(vr.begin(), vr.end(), a[i]) - vr.begin() + 1; for (int i = 1; i <= N; i++) Add1(a[i], pre[i] = Qry1(a[i] - 1) + 1); for (int i = 1; i <= N; i++) c[i] = 0; for (int i = N; i >= 1; i--) { Add1(N - a[i] + 1, suf[i] = Qry1(N - a[i]) + 1); pii cur = Qry2(a[i] + 1); if (!cur.se) cur = mp(i, 1); dec(suf[i], cur.se), Add2(a[i], cur); } for (int i = 1; i <= N; i++) add(ans, pre[i] * suf[i] % Mod); printf("%lld\n", ans); } signed main(void) { int T = read(); while (T--) Work(); return 0; }
注意到从任意一点 \(x\) 开始,其在每个区域被查的次数是固定的,设为 \(s_{x,i}\),那么 \(f(v)\) 可以写成:
\[f(v) = \sum_{i \neq z_v} \min(fine_{v,i} \times s_{v,i},pass_{v,i}) \]但是询问很奇怪,直接写成上式并不好处理询问。考虑在同一个询问中的所有点它们的 \(s_{v,i}\),对于这些点来说,往上走时经过的所有区域在时间轴上都形成一段区间,而 \(s_{v,i}\) 计算的就是在这段区间中 \(T\) 的倍数点出现的次数。考虑把这些线段在 \(\mathrm{mod} \ T\) 意义下处理,根据每个点的深度不同,时间轴和线段的位置也不相同。设在询问中 \(s_{v,i}\) 的最小值为 \(mn_{i}\),容易发现 \(s_{v,i}\) 的值只可能为 \(mn_i\) 或 \(mn_i + 1\),并且当线段在 \(\mathrm{mod} \ T\) 意义下左右端点相对位置不变时取 \(mn_i\),否则取 \(mn_i + 1\)。
也就是说,在同一次询问中,不同的 \(s_{v,i}\) 至多只有 \(O(2^k)\) 种,但还是太多了。更进一步地,考虑一个类似扫描线的过程,不难发现线段的移动是连续的,同样在 \(\mathrm{mod} \ T\) 意义考虑,取 \(mn_i\) 的位置恰是一个区间!也就是说,这 \(k\) 个区域把 \(\mathrm{mod} \ T\) 的环分成了 \(O(k)\) 个部分,这样在同一次询问中只会有 \(O(k)\) 种不同的 \(s_{v,i}\)。
接下来只需要模拟这个过程就行了:对每个位置预处理出 \(s_{v,i}\),然后对每个位置处理出一个二进制状态 \(\mathrm{mask}\) 表示每个区域是取 \(mn_i\) 还是取 \(mn_{i+1}\),这样的状态至多只有 \(O(k)\) 个。然后从下往上合并出每次询问需要的状态,处理询问就很容易了。时间复杂度 \(O(nk \log k + qk^2)\),听说能更快,但反正过了就不管了。
/* 最黯淡的一个 梦最为炽热 万千孤单焰火 让这虚构灵魂鲜活 至少在这一刻 热爱不问为何 存在为将心声响彻 */ #include <bits/stdc++.h> #define pii pair<int, int> #define mp(x, y) make_pair(x, y) #define pb push_back #define eb emplace_back #define fi first #define se second #define int long long #define mem(x, v) memset(x, v, sizeof(x)) #define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n)) #define lob lower_bound #define upb upper_bound using namespace std; inline int read() { int x = 0, w = 1;char ch = getchar(); while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); } while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * w; } inline int min(int x, int y) { return x < y ? x : y; } inline int max(int x, int y) { return x > y ? x : y; } const int MN = 2e5 + 5; const int MK = 30; const int Inf = 2e18; const int Mod = 1e9 + 7; inline int qPow(int a, int b = Mod - 2, int ret = 1) { while (b) { if (b & 1) ret = ret * a % Mod; a = a * a % Mod, b >>= 1; } return ret; } // #define dbg int N, T, K, Q, zone[MN], pass[MN], fine[MN]; char s[MN]; int dis[MN], fa[MN], ord[MN], dfc, up[MN], lim[MN], coef[MN][MK], mask[MN]; vector <pii> G[MN]; vector <int> vr[MN]; inline void DFS(int u) { ord[++dfc] = u; for (auto [v, w] : G[u]) { if (v == fa[u]) continue; fa[v] = u; dis[v] = dis[u] + w; DFS(v); } } inline void Work() { N = read(); for (int i = 1; i < N; i++) { int u = read(), v = read(), w = read(); G[u].pb(mp(v, w)); G[v].pb(mp(u, w)); } fa[1] = -1; DFS(1); K = read(); scanf("%s", s + 1); for (int i = 1; i <= N; i++) zone[i] = s[i] - 'A' + 1; for (int i = 1; i <= K; i++) pass[i] = read(); for (int i = 1; i <= K; i++) fine[i] = read(); T = read(); mem(up, -1); for (int j = 1; j <= dfc; j++) { int i = ord[j]; if (fa[i] != -1) { if (zone[i] != zone[fa[i]]) { up[i] = fa[i]; } else { up[i] = up[fa[i]]; } } else { up[i] = -1; } int p = up[i]; while (p != -1) { int nxt = up[p]; int L = dis[i] - dis[p]; int R = (nxt == -1 ? dis[i] : dis[i] - dis[nxt]); int c = (R + T - 1) / T - (L + T - 1) / T; int mx = (R - L + T - 1) / T; coef[i][zone[p]] += mx; if (c < mx) { mask[i] |= (1 << zone[p]); } p = nxt; } } for (int j = dfc; j >= 1; j--) { int i = ord[j]; vr[i].pb(mask[i]); sort(vr[i].begin(), vr[i].end()); vr[i].resize(unique(vr[i].begin(), vr[i].end()) - vr[i].begin()); if (fa[i] != -1 && zone[i] == zone[fa[i]]) { vr[fa[i]].insert(vr[fa[i]].end(), vr[i].begin(), vr[i].end()); } } for (int i = 1; i <= K; i++) lim[i] = pass[i] / fine[i]; Q = read(); while (Q--) { int op = read(); if (op == 1 || op == 2) { string ch; cin >> ch; int id = ch[0] - 'A' + 1; (op == 1 ? pass[id] : fine[id]) = read(); lim[id] = pass[id] / fine[id]; } else { int u = read(); int ans = Inf; for (int ms : vr[u]) { int cur = 0; for (int j = 1; j <= K; j++) { int c = coef[u][j]; if (ms & (1 << j)) c--; if (c <= lim[j]) { cur += c * fine[j]; } else { cur += pass[j]; } } ans = min(ans, cur); } printf("%lld\n", ans); } } } signed main(void) { int T = 1; while (T--) Work(); return 0; }