ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

abc265

2022-09-08 20:32:27  阅读:168  来源: 互联网

标签:vector int sum leq abc265 query mathcal


\(\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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有