← 全部文章

AtCoder Grand Contest 035 题解

本文目录(18 节)
  1. XOR Circle
  2. Even Degrees
  3. Skolem XOR Tree
  4. 题意
  5. 题解
  6. 代码
  7. Add and Remove
  8. 题意
  9. 题解
  10. 代码
  11. Develop
  12. 题意
  13. 题解
  14. 代码
  15. Two Histograms
  16. 题意
  17. 题解
  18. 代码

AtCoder Grand Contest 035

XOR Circle

随便判一下。

const int N = 1e5 + 7;
int n, a[N];

int main() {
	rd(n), rda(a, n);
	map<int, int> c;
	for (int i = 1; i <= n; i++) ++c[a[i]];
	if (c[0] == n) return prints("Yes"), 0;
	if (n % 3) return prints("No"), 0;
	for (pi o : c)
		if (o.se % (n / 3)) return prints("No"), 0;
	int k = 0;
	for (pi o : c) {
		int t = o.se / (n / 3);
		while (t--) k ^= o.fi;
	}
	if (k) return prints("No"), 0;
	prints("Yes");
	return 0;
}

Even Degrees

随便定向之后,若有偶数个奇点,则每次找到两个奇点之间的一条路径,对路径上的边取反即可消掉这两个奇点。

奇点个数的奇偶性等于 mm 的奇偶性,直接判。

考虑如何构造方案,找出图的一棵生成树,在树上让所有奇点与根之间的路径上的边取反即可。

const int N = 1e5 + 7;
int n, m, x[N], y[N];
bool d[N], v[N];
vector<pi> e[N];

bool dfs(int x) {
	v[x] = 1;
	bool s = d[x];
	for (pi o : e[x]) {
		int y = o.fi, i = o.se;
		if (v[y]) continue;
		bool k = dfs(y);
		if (k) swap(::x[i], ::y[i]);
		s ^= k;
	}
	return s;
}

int main() {
	rd(n, m);
	if (m & 1) return print(-1), 0;
	for (int i = 1; i <= m; i++)
		rd(x[i], y[i]), d[x[i]] ^= 1,
		e[x[i]].pb(mp(y[i], i)), e[y[i]].pb(mp(x[i], i));
	dfs(1);
	for (int i = 1; i <= m; i++) print(x[i], y[i]); 
	return 0;
}

Skolem XOR Tree

题意

  • 构造一棵 2n2n 个点的树,满足:
    • 对于 i∈[1,n]i \in [1,n],将 ii 和 i+ni+n 的点权设为 ii。
    • ii 与 i+ni+n 在树上的路径上经过的所有点的点权异或和为 ii。
  • n≤105n \le 10^5。

题解

若 nn 为 2i2^i 则无解。

否则,若 nn 为奇数,则对于 ii 为偶数,连边 (i,i+1),(i+1,n+1),(n+1,i+n),(i+n,i+1+n)(i,i+1),(i+1,n+1),(n+1,i+n),(i+n,i+1+n),最后连边 (1,2)(1,2)。

若 nn 为偶数,在上面的图的基础上挂上 nn 和 2n2n 即可,随便构造。

代码

int main() {
	int n;
	rd(n);
	if (n - (n & -n) == 0) return prints("No"), 0;
	prints("Yes");
	print(1, 2);
	for (int i = 2; i < n - !(n & 1); i += 2)
		print(i, i + 1), print(i + 1, n + 1), print(n + 1, i + n), print(i + n, i + 1 + n);
	if (!(n & 1)) {
		int x = n & -n, y = n - x;
		print(n, x + 1), print(n * 2, y + n);
	}
	return 0;
}

Add and Remove

题意

  • 给定 nn 个数 a1…na_{1\dots n}。
  • 你需要进行若干次下列操作,直到只剩下两个数:
    • 选择三个连续的数 ai−1,ai,ai+1a_{i-1},a_i,a_{i+1}。
    • 将 ai−1,ai+1a_{i-1},a_{i+1} 都加上 aia_i,然后删掉 aia_i。
  • 要求最小化最终剩下的两个数的和。
  • n≤18n \le 18。

题解

显然答案是对于每个 aia_i 乘上一个系数 xix_i。

倒序考虑整个过程,一开始只有两个元素 a1,ana_1,a_n,这两个元素的系数 x1=xnx_1 = x_n 都为 11。若在 aia_i 和 aja_j 中插入了一个元素,其中 aia_i 的系数为 xix_i,aja_j 的系数为 xjx_j,则可以发现插入的元素系数为 xi+xjx_i + x_j。

考虑 DP,我们设 f(i,j,l,r)f(i,j,l,r) 表示对于区间 [i,j][i,j],xi=lx_i = l,xj=rx_j = r 时 (i,j)(i,j) 对答案的最小贡献,转移时枚举断点 d∈(i,j)d \in (i,j) 即可。

状态数的上界为 O(n22n)\mathcal O(n^22^n)。

代码

const int N = 19;
int n, a[N];

ll f(int i, int j, int l, int r) {
	ll ans = 1e18;
	if (j - i <= 1) return 0;
	for (int d = i + 1; d < j; d++)
		ans = min(ans, f(i, d, l, l + r) + f(d, j, l + r, r) + 1ll * a[d] * (l + r));
	return ans;
}

int main() {
	rd(n), rda(a, n);
	print(f(1, n, 1, 1) + a[1] + a[n]);
	return 0;
}

Develop

题意

  • 给定 n,kn,k。
  • 设 SS 为整数的全集,求通过若干次下列操作可以得到多少个不同的集合:
    • 选择 x∈S∩[1,n]x \in S \cap [1,n],将 xx 从 SS 中去掉。
    • 对于 x−2x-2 和 x+kx+k,若不在 SS 中,则加进 SS。
  • 1≤k≤n≤1501 \le k \le n \le 150,答案对 PP 取模。

题解

考虑一个编号为 1∼n1 \sim n 的有向图,连边 (x,x−2)(x,x-2) 和 (x,x+k)(x,x+k)(如果存在)。

则每次操作相当于将一个点染黑,将其后继染白,其中初始时所有点都是白的,问最终形成的图有多少种方案。

显然黑点不能构成环,所以黑点一定是个 DAG,那么按照拓扑序选择黑点即可,于是问题等价于求给定图不存在黑环的方案数。

对 kk 分奇偶考虑然后简单 DP 一下即可。

代码

const int N = 157;
int n, K;
modint f[N][N], g[N][N][N], ans, ans0, ans1;

int main() {
	rd(n, K, P);
	if (K & 1) {
		g[0][0][0] = 1;
		int m = (K >> 1) + ((n + 1) >> 1);
		for (int i = 0; i < m; i++)
			for (int j = 0; j <= n >> 1; j++)
				for (int k = 0; k <= K + 1; k++) {
					g[i+1][0][0] += g[i][j][k];
					if (i < n >> 1) g[i+1][j+1][0] += g[i][j][k];
					if (i >= K >> 1) g[i+1][0][k?k+1:0] += g[i][j][k];
					if (i >= (K >> 1) && i < (n >> 1)) g[i+1][j+1][max(j+2,k+1)] += g[i][j][k];
				}
		for (int j = 0; j <= n >> 1; j++)
			for (int k = 0; k <= K + 1; k++)
				ans += g[m][j][k];
		print(ans);
	} else {
		f[0][0] = 1;
		for (int i = 0; i < n; i++)
			for (int j = 0; j <= min(i, K >> 1); j++)
				f[i+1][j+1] += f[i][j], f[i+1][0] += f[i][j];
		for (int j = 0; j <= K >> 1; j++)
			ans0 += f[n>>1][j], ans1 += f[(n+1)>>1][j];
		print(ans0 * ans1);
	}
	return 0;
}

Two Histograms

题意

  • 对于一个 n×mn \times m 的矩阵,一开始每个位置上都是 00。
  • 对于每一行,选择最左边的 0∼m0 \sim m 个位置 +1+1;对于每一列,选择最上边的 0∼n0 \sim n 个位置 +1+1。
  • 求最后能形成的矩阵方案数。
  • n,m≤5×105n,m \le 5 \times 10^5,答案对 998244353998244353 取模。

题解

注意到只有 kx=yk_x = y,ly=x−1l_y = x - 1 和 kx=y−1k_x = y - 1,ly=xl_y = x 才会形成一样的矩阵,那么枚举满足条件的 (x,y)(x,y) 对数容斥: ans=∑i=0min⁡(n,m)(−1)i(ni)(mi)i!(m+1)n−i(n+1)m−i ans = \sum_{i=0}^{\min(n,m)} (-1)^i \binom ni \binom mi i!(m+1)^{n-i}(n+1)^{m-i} 时间复杂度 O(n)\mathcal O(n)。

代码

int main() {
	int n, m;
	rd(n, m), init(max(n, m) + 1);
	modint a = (modint)(m + 1) ^ n, b = (modint)(n + 1) ^ m, w = 1, ans;
	for (int i = 0; i <= min(n, m); i++, a *= v[m+1], b *= v[n+1], w = -w)
		ans += w * binom(n, i) * binom(m, i) * p[i] * a * b; 
	print(ans);
	return 0;
}

标签:DP、区间 DP、容斥、构造、组合计数

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…