abc265
作者:互联网
\(\textbf{F.}\)
设 \(f(i, x, y)\) 表示考虑前 \(i\) 维, 当前和 \(P\) 的曼哈顿距离为 \(x\), 和 \(Q\) 的曼哈顿距离为 \(y\) 的方案数.
则 \(f(i, x, y) = \sum _ {s = -2000} ^ {2000} f(i - 1, x - |s - p _ i|, y - |s - q _ i|)\).
按照 \(s < \min(p _ i, q _ i)\), \(s > \max(p _ i, q _ i)\), \(\min(p _ i, q _ i) \leq s \leq \max(p _ i, q _ i)\) 三分类, 每部分分别做前缀和优化即可. 时间复杂度 \(\mathcal{O}(n V ^ 2)\).
void solve(){
int n, d; cin >> n >> d;
vector<int> p(n + 1); for(int i = 1; i <= n; i ++) cin >> p[i];
vector<int> q(n + 1); for(int i = 1; i <= n; i ++) cin >> q[i];
vector<vector<vector<modint>>> dp(n + 1, vector<vector<modint>>(d + 1, vector<modint>(d + 1)));
dp[0][0][0] = modint(1);
for(int i = 1; i <= n; i ++){
vector<vector<modint>> sumX(d + 1, vector<modint>(d + 1));
for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++){
if(x && y) sumX[x][y] = sumX[x - 1][y - 1];
int s = min(p[i], q[i]) - 1;
if(p[i] - s <= x && q[i] - s <= y)
sumX[x][y] += dp[i - 1][x - (p[i] - s)][y - (q[i] - s)];
}
vector<vector<modint>> sumY(d + 1, vector<modint>(d + 1));
for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++){
if(x && y) sumY[x][y] = sumY[x - 1][y - 1];
int s = max(p[i], q[i]) + 1;
if(s - p[i] <= x && s - q[i] <= y)
sumY[x][y] += dp[i - 1][x - (s - p[i])][y - (s - q[i])];
}
vector<vector<modint>> sumZ(d + 1, vector<modint>(2 * d + 1));
int P = min(p[i], q[i]), Q = max(p[i], q[i]);
for(int x = 0; x <= d; x ++) for(int y = 0; y <= 2 * d; y ++){
if(x && y < 2 * d) sumZ[x][y] = sumZ[x - 1][y + 1];
if(y - (Q - P) >= 0 && y - (Q - P) <= d) sumZ[x][y] += dp[i - 1][x][y - (Q - P)];
if(x - (Q - P) - 1 >= 0 && y < d) sumZ[x][y] -= dp[i - 1][x - (Q - P) - 1][y + 1];
}
for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++)
dp[i][x][y] = sumX[x][y] + sumY[x][y] + sumZ[x][y];
}
modint ans(0);
for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++)
ans += dp[n][x][y];
cout << ans.val() << "\n";
}
\(\textbf{G.}\)
线段树维护区间 \(0, 1, 2\) 的个数和 \(01, 02, 10, 12, 20, 21\) 的个数即可.
时间复杂度 \(\mathcal{O}(n + q \log n)\).
struct Sgt{
private:
#define ls(o) 2 * o
#define rs(o) 2 * o + 1
struct Item{
array<int, 3> once; array<array<ll, 3>, 3> twice;
} ;
vector<Item> tree; vector<array<int, 3>> lazy; int n;
Item merge(Item ls, Item rs){
Item res;
for(int i = 0; i < 3; i ++)
res.once[i] = ls.once[i] + rs.once[i];
for(int i = 0; i < 3; i ++) for(int j = 0; j < 3; j ++)
res.twice[i][j] = ls.twice[i][j] + rs.twice[i][j] + (ll)ls.once[i] * rs.once[j];
return res;
}
void pushup(int o){
tree[o] = merge(tree[ls(o)], tree[rs(o)]);
}
void addtag(int o, array<int, 3> mov){
Item lst = tree[o];
for(int i = 0; i < 3; i ++) tree[o].once[i] = 0;
for(int i = 0; i < 3; i ++) for(int j = 0; j < 3; j ++) tree[o].twice[i][j] = 0;
for(int i = 0; i < 3; i ++)
tree[o].once[mov[i]] += lst.once[i];
for(int i = 0; i < 3; i ++) for(int j = 0; j < 3; j ++)
tree[o].twice[mov[i]][mov[j]] += lst.twice[i][j];
array<int, 3> lzy = lazy[o];
for(int i = 0; i < 3; i ++) lazy[o][i] = mov[lzy[i]];
}
void pushdown(int o){
addtag(ls(o), lazy[o]), addtag(rs(o), lazy[o]); lazy[o] = {0, 1, 2};
}
public:
Sgt(int _n = 0, vector<int> val = {}){
n = _n, tree = vector<Item>(4 * n + 100), lazy = vector<array<int, 3>>(4 * n + 100, {0, 1, 2});
auto build = [&](auto build, int o, int L, int R) -> void {
if(L == R) return tree[o].once[val[L]] ++, void();
int M = (L + R) / 2; build(build, ls(o), L, M), build(build, rs(o), M + 1, R); pushup(o);
} ;
build(build, 1, 0, n - 1);
}
void modify(int l, int r, array<int, 3> mov){
auto modify = [&](auto modify, int o, int L, int R) -> void {
if(l <= L && R <= r) return addtag(o, mov), void();
pushdown(o); int M = (L + R) / 2;
if(l <= M) modify(modify, ls(o), L, M);
if(r > M) modify(modify, rs(o), M + 1, R);
pushup(o);
} ;
modify(modify, 1, 0, n - 1);
}
Item query(int l, int r){
auto query = [&](auto query, int o, int L, int R) -> Item {
if(l <= L && R <= r) return tree[o];
pushdown(o); int M = (L + R) / 2;
if(r <= M) return query(query, ls(o), L, M);
if(l > M) return query(query, rs(o), M + 1, R);
return merge(query(query, ls(o), L, M), query(query, rs(o), M + 1, R));
} ;
return query(query, 1, 0, n - 1);
}
} ;
void solve(){
int n, q; cin >> n >> q;
vector<int> a(n + 1); for(int i = 1; i <= n; i ++) cin >> a[i];
Sgt sgt(n + 1, a);
while(q --){
int opt; cin >> opt;
if(opt == 1){
int L, R; cin >> L >> R; auto res = sgt.query(L, R);
cout << res.twice[1][0] + res.twice[2][0] + res.twice[2][1] << "\n";
}
if(opt == 2){
int L, R, S, T, U; cin >> L >> R >> S >> T >> U;
sgt.modify(L, R, {S, T, U});
}
}
}
\(\textbf{H.}\)
先考虑先手获胜的充要条件.
设第 \(i\) 行中先手的棋子位于格子 \(p _ i\), 后手的格子位于格子 \(q _ i\). 则 \(p _ i \neq q _ i\).
- 当 \(p _ i > q _ i\) 时. 则此时相当于有一堆 \(p _ i - q _ i - 1\) 个石头构成的堆, 每次每人可以取走正整数枚棋子. 称这样一行为关键行.
- 当 \(p _ i < q _ i\) 时. 则此时先手, 后手互不干扰且双方都想用这个棋子多走几轮. 于是此时先手可以多走 \(p _ i - 1\) 轮, 后手可以多走 \(w - q _ i\) 轮. 称这一行为非关键行.
设\(P = \sum _ {i = 1} ^ {h} (p _i - 1), Q = \sum _ {i = 1} ^ {h} (w - q _ i)\).
-
若 \(P > Q\). 则先手第一步任意操作关键行. 对于后手的每一步:
- 若其操作关键行, 则先手尽量操作关键行, 若不行则操作非关键行. 则之后双方都只能操作非关键行, 因为 \(P > Q\), 所以后手一定会比先手先操作完, 从而输掉比赛.
- 若其操作非关键行, 则先手同样操作非关键行, 此时 \(P - Q\) 不变.
于是此时先手必胜. (简而言之就是无论关键行谁赢, 最后先手都能耗死后手)
-
若 \(P < Q\). 同理可知此时后手必胜.
-
若 \(P = Q\). 则对于双方来说, 非关键行都是无用的. (因为若一方操作非关键行, 另一方必然操作关键行, 根据上面的讨论从而获胜. 故双方都只会在关键行操作完之后去操作非关键行, 此时的输赢和关键行内的输赢是相同的)
注意到关键行本质上是一个若干堆石头的
Nim
游戏, 所以此时先手获胜当且仅当 \(\bigoplus _ {i, p _ i > q _ i} (p _ i - q _ i - 1) \neq 0\).
所以设 \(S = \bigoplus _ {i, p _ i > q _ i} (p _ i - q _ i - 1)\), 则只需求 \(P > Q\) 或 \(P = Q\) 且 \(S > 0\) 的方案数.
设 \(f(i, T, S)\) 表示前 \(i\) 行, \(\sum _ {j = 1} ^ {i} (p _ j - 1) - \sum _ {j - 1} ^ {i} (w - q _ j) = T\), \(\bigoplus _ {1 \leq j \leq i, p _ j > q _ j} (p _ j - q _ j - 1) = S\) 的方案数.
则转移为 \(\begin{cases} \forall 1 \leq p < q \leq w, f(i, T, S) \xrightarrow{\sum} f(i + 1, T + (p - 1) - (w - q), S) \\ \forall 1 \leq q < p \leq w, f(i, T, S) \xrightarrow{\sum} f(i + 1, T, S \oplus (p - q - 1)) \end{cases}\).
可以优化为 \(\begin{cases} \forall - \! w \leq t \leq w, \left \lfloor \dfrac{w - |t|}{2} \right \rfloor f(i, T, S) \xrightarrow{\sum} f(i + 1, T + t, S) \\ \forall 0 \leq s \leq w - 1, (w - s - 1) f(i, T, S) \xrightarrow{\sum} f(i + 1, T, S \oplus s) \end{cases}\).
边界为 \(f(0, 0, 0) = 1\); 答案为 \(\sum _ {T > 0 \text{ or } T = 0 \text{ and } S > 0} f(h, T, S)\).
直接计算, 复杂度为 \(\mathcal{O}(h \cdot hw \cdot w \cdot w) = \mathcal{O}(h ^ 2 w ^ 3)\). 考虑优化.
事实上, \(T\) 的转移和 \(S\) 的转移互相独立. 考虑分开dp再合并起来.
于是设 \(f _ T (i, T)\) 表示有 \(i\) 行关键行, \(\sum _ {j = 1} ^ {i} (p _ j - 1) - \sum _ {j - 1} ^ {i} (w - q _ j) = T\) 的方案数.
设 \(f _ S (i, S)\) 表示有 \(i\) 行非关键行, \(\bigoplus _ {1 \leq j \leq i, p _ j > q _ j} (p _ j - q _ j - 1) = S\) 的方案数.
则转移为 \(\begin{cases} \forall - \! w \leq t \leq w, \left \lfloor \dfrac{w - |t|}{2} \right \rfloor f _ T (i, T) \xrightarrow{\sum} f _ T (i + 1, T + t) \\ \forall 0 \leq s \leq w - 1, (w - s - 1) f _ S (i, T, S) \xrightarrow{\sum} f _ S (i + 1, S \oplus s) \end{cases}\).
边界为 \(f _ T (0, 0) = f _ S (0, 0) = 1\); 答案为 \(\sum _ {k = 0} ^ {h} \sum _ {T > 0 \text{ or } T = 0 \text{ and } S > 0} f _ T (k, T) f _ S (h - k, S)\).
直接计算, 计算 \(f _ T\) 的复杂度为 \(\mathcal{O}(h \cdot hw \cdot w) = \mathcal{O}(h ^ 2 w ^ 2)\); 计算 \(f _ S\) 的复杂度为 \(\mathcal{O}(h \cdot w \cdot w) = \mathcal{O}(h w ^ 2)\); 计算答案的复杂度为 \(\mathcal{O}(h \cdot hw \cdot w) = \mathcal{O}(h ^ 2 w ^ 2)\).
先考虑优化答案的计算.
注意到答案为 \(\sum _ {k = 0} ^ {h} \sum _ {T > 0} f _ T (k, T) \left ( \dfrac{w (w - 1)}{2} \right ) ^ {h - k} + \sum _ {k = 0} ^ {h} f _ T (k, 0) \sum _ {S > 0} f _ S (h - k, S)\) 即可 \(\mathcal{O}(h \cdot hw + h \cdot w) = \mathcal{O}(h ^ 2 w)\) 计算.
再考虑优化 \(f _ T\) 的复杂度.
此时 \(f _ T (i, T) = \sum _ {-w \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor f _ T(i - 1, T - t)\).
简便期间设 \(g _ T (T) = f _ T (i - 1, T)\). 考虑计算 \(\Delta = f _ T (i, T) - f _ T (i, T - 1)\).
则 \(\begin{array}{l} \Delta & = f _ T (i, T) - f _ T (i, T - 1) = \sum _ {-w \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor (g _ T (T - t) - \ g _ T(T - 1 - t)) \\ & = \sum _ {-w \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor g _ T (T - t) - \sum _ {-w + 1 \leq t \leq w + 1} \left \lfloor \dfrac{w - |t - 1|}{2} \right \rfloor g _ T (T - t) \\ & = \sum _ {-w + 1 \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor g _ T (T - t) - \sum _ {-w + 1 \leq t \leq w} \left \lfloor \dfrac{w - |t - 1|}{2} \right \rfloor g _ T (T - t) \\ & = - \sum _ {1 \leq t \leq w} [2 \nmid w - t] g _ T (T - t) + \sum _ {-w + 1 \leq t \leq 0} [2 \mid w + t] g _ T (T - t) \end{array}\).
所以当 \(w\) 是奇数时, \(\Delta = g _ T (T + w - 2) + \cdots + g _ T (T + 1) - g _ T(T - 2) - \cdots - g _ T (T - w + 1)\).
当 \(w\) 是偶数时, \(\Delta = g _ T (T + w - 2) + \cdots + g _ T (T) - g _ T (T - 1) \cdots - g _ T (T - w + 1)\).
所以对 \(g _ T (*)\) 做奇偶分组的前缀和即可 \(\mathcal{O}(1)\) 计算 \(\Delta\).
而 \(f _ T (i, 0)\) 显然可以 \(\mathcal{O}(w)\) 计算. 所以复杂度变为 \(\mathcal{O}(h \cdot (w + hw)) = \mathcal{O}(h ^ 2 w)\).
所以就做完了. 总时间复杂度 \(\mathcal{O}(h ^ 2 w + h w ^ 2)\). 注意常数.
- 根据 \(f _ T (i, T) = f _ T (i, -T)\) 可以砍去一个 \(\dfrac{1}{2}\) 的常数.
- 先 \(\mathcal{O}(h w ^ 2)\) 预处理出所有的 \(f _ S (i, S)\), 之后每计算出一个 \(f _ T (i, T)\) 就直接往答案里更新. 这样 \(f _ T (i, T)\) 可以滚动优化到第一维 \(i\). 以达到卡空间的目的.
void solve(){
int h, w; cin >> h >> w;
int maxS = 1 << (__lg(w) + 1);
vector<vector<modint>> dpS(h + 1, vector<modint>(maxS + 1));
dpS[0][0] = modint(1);
for(int i = 0; i < h; i ++)
for(int S = 0; S <= maxS; S ++)
if(dpS[i][S].val())
for(int s = 0; s < w; s ++)
dpS[i + 1][S ^ s] += dpS[i][S] * modint(w - 1 - s);
vector<modint> sumdpS(h + 1);
for(int i = 0; i <= h; i ++)
for(int S = 0; S <= maxS; S ++)
sumdpS[i] += dpS[i][S];
Binom binom(h);
modint ans(0);
vector<modint> dpT((h + 1) * w + 1); dpT[0] = modint(1);
for(int i = 0; i <= h; i ++){
for(int T = 1; T <= i * w; T ++)
ans += binom.binom(h, i) * dpT[T] * sumdpS[h - i];
for(int S = 1; S <= maxS; S ++)
ans += binom.binom(h, i) * dpT[0] * dpS[h - i][S];
if(i == h)
break;
vector<modint> sum((i + 1) * w + w + w + 1);
for(int T = -w; T <= (i + 1) * w + w; T ++){
sum[T + w] = dpT[abs(T)];
if(T >= -w + 2)
sum[T + w] += sum[T - 2 + w];
}
vector<modint> newdpT((h + 1) * w + 1);
for(int t = -w; t <= w; t ++)
newdpT[0] += dpT[abs(t)] * modint((w - abs(t)) / 2);
for(int T = 1; T <= (i + 1) * w; T ++){
if(w % 2 == 0){
newdpT[T] = newdpT[T - 1];
newdpT[T] += (sum[T + w - 2 + w] - sum[T - 2 + w]);
newdpT[T] -= (sum[T - 1 + w] - sum[T - w - 1 + w]);
}
if(w % 2 == 1){
newdpT[T] = newdpT[T - 1];
newdpT[T] += (sum[T + w - 2 + w] - sum[T - 1 + w]);
newdpT[T] -= (sum[T - 2 + w] - sum[T - w - 1 + w]);
}
}
dpT = newdpT;
}
cout << ans.val() << "\n";
}
标签:vector,int,sum,leq,abc265,query,mathcal 来源: https://www.cnblogs.com/zhangmj2008/p/abc265.html