VP Educational Codeforces Round 56 (Rated for Div. 2)


A. Dice Rolling

点击查看代码
void solve() {
    int x;
    std::cin >> x;
    if (x & 1) {
    	std::cout << 1 + (x - 3) / 2 << "\n";
    } else {
    	std::cout << x / 2 << "\n";
    }
}

B. Letters Rearranging

题意:判断字符串重新排列能不能不是回文。

只有字母都相同才无解。否则把一类字符都放到最前面。

点击查看代码
void solve() {
	std::string s;
	std::cin >> s;
	int n = s.size();    
	if (std::ranges::count(s, s[0]) == n) {
		std::cout << -1 << "\n";
		return;
	}

	for (int i = 1, j = 1; i < n && j < n; ++ i) {
		if (s[i] == s[0]) {
			continue;
		}
		j = std::max(j, i + 1);
		while (j < n && s[j] != s[0]) {
			++ j;
		}

		if (j < n) {
			std::swap(s[i], s[j]);
		}
	}

	std::cout << s << "\n";
}

C. Mishka and the Last Exam

题意:\(a\)是递增的序列。\(b_i = a_i + a_{n-i+1}\)。给出\(b\),求\(a\)

尽量给\(a_{n-i+1}\)。但需要满足条件,所以\(a_i\)也会分到一些数,保证给最少就行。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<i64> a(n);
	for (int i = 0; i < n / 2; ++ i) {
		std::cin >> a[i];
	}    

	for (int i = 0; i + 1 < n / 2; ++ i) {
		a[n - 1 - (i + 1)] += a[n - 1 - i];
		a[i + 1] -= a[n - 1 - i];
		if (a[i] < a[i + 1]) {
			a[n - 1 - (i + 1)] += a[i + 1] - a[i];
			a[i + 1] = a[i];
		}
	}

	std::ranges::reverse(a);
	for (int i = 0; i < n; ++ i) {
		std::cout << a[i] << " \n"[i == n - 1];
	}
}

D. Beautiful Graph

题意:给你一个图,每个点可以是\(1,2, 3\)其中一棵。你要使得任意两个相邻的点的和是奇数。求有多少方案。

\(1, 3\)看作黑色,\(2\)看作白色,那么就变成了黑白染色。只有二分图才有解。
一个连通块方案数就是放奇数点的方案数,因为每个奇数点有两种选择,所以乘上把黑色点作为奇数点的答案加上白色作为奇数的答案就行。

点击查看代码
const int mod = 998244353, N = 3e5 + 5;

int p2[N];

void init(int n) {
	p2[0] = 1;
	for (int i = 1; i <= n; ++ i) {
		p2[i] = (i64)p2[i - 1] * 2 % mod;
	}
}

void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<int>> adj(n);
    for (int i = 0; i < m; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    std::vector<int> c(n);
    int cnt[3]{};

    auto dfs = [&](auto & self, int u, int color) -> bool {
    	c[u] = color;
    	cnt[color] += 1;
    	bool flag = false;
    	for (auto & v : adj[u]) {
    		if (!c[v]) {	
    			flag |= self(self, v, 3 - color);
    		} else {
    			flag |= c[u] == c[v];
    		}
    	}

    	return flag;
    };

    int ans = 1;
    for (int i = 0; i < n; ++ i) {
    	if (!c[i]) {
    		cnt[1] = cnt[2] = 0;
    		if (dfs(dfs, i, 1)) {
    			std::cout << 0 << "\n";
    			return;
    		}

    		int sum = (p2[cnt[1]] + p2[cnt[2]]) % mod;
    		ans = (i64)ans * sum % mod;
    	}
    }

    std::cout << ans << "\n";
}

F. Vasya and Array

题意:给你一个长度为\(n\)的数组,值域为\([1, k]\)。有些位置是\(-1\)代表未填。你需要填上这些数,使得没有长度大于等于\(len\)的子数组相同。求方案数。

一个显然的\(dp\)是,记\(f[i][j][l]\)表示第\(i\)个位置填了\(j\)末尾相同数长度为\(l\)的合法方案数,转移是\(f[i][j][l] = f[i - 1][j][l - 1], f[i][j][1] = \sum_{x=1}^{k} \sum_{y=1}^{i-1} f[i - 1][x][y](x \ne j)\)。这样无法通过本题。
考虑优化,记\(f[i][j]\)为第\(i\)个位置填了\(j\)的合法方案数,\(g[i] = \sum_{x = 1}^{i} \sum_{y=1}^{k} f[x][y]\)。那么我们先直接让\(f[i][j] = g[i - 1]\)。这样发现有不合法的方案被算进来了,通过分析,幸运的发现不合法状态只有\([a_{i-len + 1}, a_i]\)都是\(-1\)或者\(j\)才会产生,那么如果符合这个条件,我们多算了\(g[i - len]\),但需要注意的是,\(g[i - len]\)包含了\(f[i - len][j]\),如果不合法状态包括它,那么在\(f[i - 1][j]\)的时候就会减去它,也就是不合法状态会在第一次出现就被减去,所以我们还需要加回来\(f[i - len][j]\)
于是得到\(f[i][j] = g[i - 1] - (g[i - len] + f[i - len][j])(如果[a_{i-len + 1}, a_i]都是-1或者j))\)

点击查看代码
const int mod = 998244353;

void solve() {
    int n, k, len;
    std::cin >> n >> k >> len;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector l(n + 1, std::vector<int>(k + 1));
    for (int i = 1; i <= n; ++ i) {
    	for (int j = 1; j <= k; ++ j) {
    		if (a[i] == j || a[i] == -1) {
    			l[i][j] = l[i - 1][j] + 1;
    		}
    	}
    }

    std::vector f(n + 1, std::vector<int>(k + 1));
    std::vector<int> g(n + 1);
    g[0] = 1;
    for (int i = 1; i <= n; ++ i) {
    	for (int j = 1; j <= k; ++ j) {
    		if (a[i] != j && a[i] != -1) {
    			continue;
    		}

    		f[i][j] = g[i - 1];
    		if (l[i][j] >= len) {
    			f[i][j] = (f[i][j] - g[i - len] + mod) % mod;
    			f[i][j] = (f[i][j] + f[i - len][j]) % mod;
    		}

    		g[i] = (g[i] + f[i][j]) % mod;
    	}
    }

    std::cout << g[n] << "\n";
}

G. Multidimensional Queries

题意:给你\(n\)\(k\)维点,每次操作会修改一个点,或者查询\([l, r]\)里曼哈顿距离最大的两个点的距离。

注意\(k\)很小。
回想二维点对的最大曼哈顿距离怎么求,其实就是拆绝对值变成了\(4\)种情况:\(x_i - x_j + y_i - y_j, x_i - x_j - y_i + y_j, -x_i + x_j + y_i - y_j, -x_i + x_j - y_i + y_j\)
同理扩展到\(k\)维,两个点距离可以有\(2^k\)次方种情况,他们的曼哈顿距离就是其中最大的,我们可以把一个点的坐标单独拿出来,每个点的一个坐标只有正负两种情况。那么我们可以用二进制表示第\(i\)个坐标是正还是负。然后如果这个点第\(i\)坐标是正,那么另一个点第\(i\)个坐标就是负的。发现二进制下它们互为补集。那么可以用线段树维护。

点击查看代码
#define ls (u << 1)
#define rs (u << 1 | 1)
#define umid (tr[u].l + tr[u].r >> 1)

template <class Info>
struct Node {
	int l, r;
	Info info;
};

template <class Info>
struct SegmentTree {
	std::vector<Node<Info> > tr;
	SegmentTree(int _n) {
		init(_n);
	}

	SegmentTree(std::vector<Info> & a) {
		init(a);
	}

	void init(int _n) {
		tr.assign(_n << 2, {});
		build(0, _n - 1);
	}

	void init(std::vector<Info> & a) {
		int _n = (int)a.size();
		tr.assign(_n << 2, {});
		build(0, _n - 1, a);
	}

	void pushup(int u) {
		tr[u].info = tr[ls].info + tr[rs].info;
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, ls); build(mid + 1, r, rs);
	}

	void build(int l, int r, std::vector<Info> & a, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			tr[u].info = a[l];
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, a, ls); build(mid + 1, r, a, rs);
		pushup(u);
	}

	void modify(int p, Info add, bool set = false) {
		int u = 1;
		while (tr[u].l != tr[u].r) {
			int mid = umid;
			if (p <= mid) {
				u = ls;
			} else {
				u = rs;
			}
		}

		if (set) {
			tr[u].info = add;
		} else {
			tr[u].info = tr[u].info + add;
		}

		u >>= 1;
		while (u) {
			pushup(u);
			u >>= 1;
		}
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		int mid = umid;
		if (r <= mid) {
			return query(l, r, ls);
		}  else if (l > mid) {
			return query(l, r, rs);
		}

		return query(l, r, ls) + query(l, r, rs);
	}
};

struct Info {
	std::array<int, 1 << 5> max;
	Info() {}
	Info(const std::array<int, 5> & a) {
		for (int s = 0; s < 1 << 5; ++ s) {
			max[s] = 0;
			for (int i = 0; i < 5; ++ i) {
				if (s >> i & 1) {
					max[s] += a[i];
				} else {
					max[s] -= a[i];
				}
			}
		}
	}
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	for (int s = 0; s < 1 << 5; ++ s) {
		res.max[s] = std::max(a.max[s], b.max[s]);
	}
	return res;
}

void solve() {
    int n, k;
    std::cin >> n >> k;
    const int K = (1 << 5) - 1;
    std::vector<Info> info(n);
    for (int i = 0; i < n; ++ i) {
    	std::array<int, 5> a{};
    	for (int j = 0; j < k; ++ j) {
    		std::cin >> a[j];
    	}

    	info[i] = Info(a);
    }

    SegmentTree<Info> tr(info);

    int q;
    std::cin >> q;
    while (q -- ) {
    	int op;
    	std::cin >> op;
    	if (op == 1) {
    		int p;
    		std::cin >> p;
    		-- p;
    		std::array<int, 5> a{};
    		for (int i = 0; i < k; ++ i) {
    			std::cin >> a[i];
    		}

    		Info info(a);
    		tr.modify(p, info, true);
    	} else {
    		int l, r;
    		std::cin >> l >> r;
    		-- l, -- r;
    		auto a = tr.query(l, r).max;
    		int ans = 0;
    		for (int i = 0; i < 1 << 5; ++ i) {
    			ans = std::max(ans, a[i] + a[~i & K]);
    		}

    		std::cout << ans << "\n";
    	}
    }
}
posted @ 2025-04-10 16:31  maburb  阅读(16)  评论(0)    收藏  举报