C/C++教程

abc265

本文主要是介绍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";
}
这篇关于abc265的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!