【CodeForces】Codeforces Round 618

比赛链接

点击打开链接

官方题解

点击打开链接

Problem A. Anu Has a Function

讨论 f ( a , b )   ( a , b { 0 , 1 } ) f(a,b)\ (a,b\in\{0,1\}) 的结果,可以发现,答案的各个位是独立的。

进一步发现,答案在某一位上是 1 1 当且仅当 a 1 a_1 是唯一一个在这一位上是 1 1 的数。
因此, a 2 , a 3 , , a N a_2,a_3,\dots,a_N 的排列顺序是不重要的,枚举 a 1 a_1 ,并计算对应的答案,取最大者即可。

时间复杂度 O ( N ) O(N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int a[MAXN], pre[MAXN], suf[MAXN];
int main() {
	int n; read(n);
	int ans = 0, pos = 1;
	for (int i = 1; i <= n; i++) {
		read(a[i]);
		pre[i] = pre[i - 1] | a[i];
	}
	for (int i = n; i >= 1; i--)
		suf[i] = suf[i + 1] | a[i];
	for (int i = 1; i <= n; i++) {
		int tmp = pre[i - 1] | suf[i + 1];
		int res = a[i] & (~tmp);
		if (res > ans) {
			ans = res;
			pos = i;
		}
	}
	swap(a[pos], a[1]);
	for (int i = 1; i <= n; i++)
		printf("%d ", a[i]);
	return 0;
}

Problem B. Aerodynamic

题目中 T T 的定义即为 P P P -P 的闵可夫斯基和。

因此,若 P , T P,T 相似,则 P P P -P 中的每一条边对应的向量都应相等。
从而答案为 Yes 当且仅当 P P 是中心对称图形。

时间复杂度 O ( N ) O(N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct point {int x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
long long operator * (point a, point b) {return 1ll * a.x * b.y - 1ll * a.y * b.x; }
ll x[MAXN], y[MAXN], sx, sy;
int main() {
	int n; read(n);
	if (n & 1) {
		puts("No");
		return 0;
	}
	for (int i = 1; i <= n; i++) {
		read(x[i]), read(y[i]);
		sx += x[i], sy += y[i];
	}
	for (int i = 1, j = n / 2 + 1; j <= n; i++, j++)
		if (x[i] * n + x[j] * n != sx * 2
		||  y[i] * n + y[j] * n != sy * 2) {
			puts("No");
			return 0;
		}
	puts("Yes");
	return 0;
}

Problem C. Water Balance

这是个 Div.1 C 题

将序列倒过来,用单调栈贪心维护各个断点的位置即可。

时间复杂度 O ( N ) O(N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
typedef long long ll;
typedef double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n; ld a[MAXN], s[MAXN];
int top, q[MAXN];
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]), s[i] = s[i - 1] + a[i];
	for (int i = n; i >= 1; i--) {
		q[++top] = i;
		while (top >= 2 && (s[q[top]] - s[i - 1]) / (q[top] - (i - 1)) > (s[q[top - 1]] - s[q[top]]) / (q[top - 1] - q[top])) top--;
	}
	q[top + 1] = 0;
	for (int i = top; i >= 1; i--) {
		ld res = (s[q[i]] - s[q[i + 1]]) / (q[i] - q[i + 1]);
		for (int j = q[i + 1] + 1; j <= q[i]; j++)
			printf("%.10lf\n", res);
	}
	return 0;
}

Problem D. Around the World

首先, 2 5 2^5 以内的数的线性基的数量不多,搜索可知,共有 374 374 种。

题目中对图性质的限制意味着若将 1 1 号点删去,与 1 1 号点相邻的各个点若没有边直接相连,将互不连通。并且与 1 1 号点相邻的各个点之间的边一定形成了一个匹配。

考虑对于给定的图,判断是否存在长为 0 0 的环,则可任取一棵生成树,对所有非树边形成的环长建立线性基,若它们线性无关,则不存在长为 0 0 的环。

由于 2 5 2^5 以内的数的线性基的数量不多,可以直接将当前线性基的种类作为状态,进行动规套动规,实现细节可以参考代码。

时间复杂度 O ( M K ) O(MK) ,其中 K = 374 K=374

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 405;
const int P = 1e9 + 7;
typedef long long ll;
typedef bitset <32> info;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
unordered_set <info> st;
unordered_map <info, int> home;
vector <pair <int, int>> a[MAXN];
info q[MAXN]; int l, r, trans[MAXM][32];
int x[MAXN], y[MAXN], z[MAXN], f[MAXN];
int sum[MAXN], from[MAXN];
vector <int> v[MAXN][2];
int find(int x) {
	if (f[x] == x) return x;
	else return f[x] = find(f[x]);
}
void dfs(int pos, int fa, int f, int s) {
	sum[pos] = s, from[pos] = f;
	for (auto x : a[pos])
		if (x.first != fa) {
			dfs(x.first, pos, f, s ^ x.second);
		}
}
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	q[l = r = 1] = 1;
	st.insert(q[1]);
	while (l <= r) {
		info tmp = q[l++];
		for (int i = 0; i <= 31; i++) {
			info res = tmp;
			for (int j = 0; j <= 31; j++)
				res[i ^ j] = (res[i ^ j] | tmp[j]);
			if (st.count(res) == 0) {
				st.insert(res);
				q[++r] = res;
			}
		}
	}
	for (int i = 1; i <= r; i++)
		home[q[i]] = i;
	for (int t = 1; t <= r; t++) {
		info tmp = q[t];
		for (int i = 0; i <= 31; i++) {
			info res = tmp;
			for (int j = 0; j <= 31; j++)
				res[i ^ j] = (res[i ^ j] | tmp[j]);
			if (res.count() == tmp.count() * 2) trans[t][i] = home[res];
			else trans[t][i] = 0;
		}
	}
	int n, m; read(n), read(m);
	for (int i = 1; i <= n; i++)
		f[i] = i;
	static int p[MAXN];
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]), read(z[i]), p[i] = i;
	}
	sort(p + 1, p + m + 1, [&] (int a, int b) {return ((x[a] == 1) || (y[a] == 1)) < ((x[b] == 1) || (y[b] == 1)); });
	static bool vis[MAXN], key[MAXN];
	for (int i = 1; i <= m; i++) {
		int pos = p[i];
		if (find(x[pos]) != find(y[pos])) {
			f[find(x[pos])] = find(y[pos]);
			a[x[pos]].emplace_back(y[pos], z[pos]);
			a[y[pos]].emplace_back(x[pos], z[pos]);
		} else vis[pos] = true;
	}
	for (auto x : a[1]) {
		dfs(x.first, 1, x.first, x.second);
		key[x.first] = true;
	}
	for (int i = 1; i <= m; i++)
		if (vis[i]) {
			int len = sum[x[i]] ^ sum[y[i]] ^ z[i];
			if (from[x[i]] == from[y[i]]) {
				v[from[x[i]]][0].push_back(len);
			} else {
				assert(from[x[i]] == 0 || from[y[i]] == 0);
				if (from[x[i]] == 0) v[from[y[i]]][1].push_back(len);
				else v[from[x[i]]][1].push_back(len);
			}
		}
	static int dp[MAXN][MAXM]; dp[0][1] = 1;
	for (int i = 1; i <= n; i++)
		if (!key[i]) memcpy(dp[i], dp[i - 1], sizeof(dp[i - 1]));
		else {
			if (v[i][1].size()) {
				for (int j = 1; j <= r; j++) {
					update(dp[i][j], dp[i - 1][j]);
					int res = j;
					for (auto x : v[i][0])
						res = trans[res][x];
					if (res != 0) update(dp[i][res], 2ll * dp[i - 1][j] % P);
					for (auto x : v[i][1])
						res = trans[res][x];
					if (res != 0) update(dp[i][res], dp[i - 1][j]);
				}
			} else {
				for (int j = 1; j <= r; j++) {
					update(dp[i][j], dp[i - 1][j]);
					int res = j;
					for (auto x : v[i][0])
						res = trans[res][x];
					if (res != 0) update(dp[i][res], dp[i - 1][j]);
				}
			}
		}
	int ans = 0;
	for (int i = 1; i <= r; i++)
		update(ans, dp[n][i]);
	cout << ans << endl;
	return 0;
}

Problem E. So Mean

由于 N N 是偶数,若询问一个大小为 N 1 N-1 的子集 S S ,答案为 Yes 当且仅当 S S 是一段连续的数字,此时 S S 中数字的平均数即为中位数。由此,我们可以得知 1 , N 1,N 的位置,由于有两种排列不能区分,任取一个作为 1 1 即可。

将剩余每个数和 1 1 一起询问一次,我们可以知道所有数的奇偶性。

考虑剩余的 N 2 N-2 个数,由于 N 2 N-2 依然是偶数,我们同样可以询问一个大小为 N 3 N-3 的子集来找到 2 , N 1 2,N-1 ,这也直接带来了一个操作次数为 O ( N 2 ) O(N^2) 的解法。
对于足够小的 N N ,问题已经得到了解决。

按照这个方式找到 1 , 2 , 3 , 4 , N 3 , N 2 , N 1 , N 1,2,3,4,N-3,N-2,N-1,N ,此时我们总共使用了 5 N 5N 次操作。

接下来,我们考虑计算出每一个未被确定的数模 3 , 5 , 7 , 8 3,5,7,8 的余数。
由于 3 × 5 × 7 × 8 = 840 800 3\times 5\times 7\times 8 =840\geq800 ,我们可以用中国剩余定理合并,得到确切的数字。

对于模 3 , 5 , 7 3,5,7 ,我们总能从已经确定的数字中找到一系列数,将它们与想要确定的数一起询问,从而判断其余数是否为某一值。例如,询问 1 , 2 , x 1,2,x 可以判断 x x 是否是 3 3 的倍数。

确定所有数模 3 3 的余数大约需要 N + 2 3 N N+\frac{2}{3}N 次操作,
确定所有数模 5 5 的余数大约需要 N + 4 5 N + 3 5 N + 2 5 N N+\frac{4}{5}N+\frac{3}{5}N+\frac{2}{5}N 次操作,
确定所有数模 7 7 的余数大约需要 N + 6 7 N + 5 7 N + 4 7 N + 3 7 N + 2 7 N N+\frac{6}{7}N+\frac{5}{7}N+\frac{4}{7}N+\frac{3}{7}N+\frac{2}{7}N 次操作。

对于模 8 8 ,由于我们已经知道了每个数模 2 2 的余数,只需要再进行两次询问,求出该数模 4 4 ,模 8 8 的余数即可。

时间复杂度 O ( N 2 ) O(N^2) ,操作次数约为 16 N 16N

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 805;
const int p[4] = {3, 5, 7, 8};
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int n, res, ans[MAXN];
bool odd[MAXN], used[MAXN];
pair <int, int> pos[5];
int gett(int x) { // Modulo 3
	cout << '?' << ' ' << 3 << ' ' << x << ' ';
	cout << pos[1].first << ' ' << pos[2].first << endl;
	read(res); if (res) return 0;
	cout << '?' << ' ' << 3 << ' ' << x << ' ';
	cout << pos[2].first << ' ' << pos[3].first << endl;
	read(res); if (res) return 1;
	return 2;
}
int getf(int x) { // Modulo 5
	bool mark[5];
	memset(mark, false, sizeof(mark));
	for (int i = 1; i <= 4; i++) {
		cout << '?' << ' ' << 5 << ' ' << x << ' ' <<
			pos[1].second << ' ' << pos[2].second << ' ' << pos[3].second << ' ';
		cout << pos[i].first << endl;
		int value = (5 - (3 * n - 3 + i) % 5) % 5;
		read(res); if (res) return value;
		mark[value] = true;
	}
	for (int i = 0; i <= 4; i++)
		if (!mark[i]) return i;
	return -1;
}
int gets(int x) { // Modulo 7
	bool mark[7]; int cnt = 0;
	memset(mark, false, sizeof(mark));
	for (int i = 1; i <= 4 && cnt <= 5; i++)
	for (int j = 1; j <= 4 && cnt <= 5; j++) {
		int value = (7 - (n * 4 + 4 - i - (n - j + 1)) % 7) % 7;
		if (mark[value]) continue;
		cout << '?' << ' ' << 7 << ' ' << x << ' ';
		for (int k = 1; k <= 4; k++)
			if (k != i) cout << pos[k].first << ' ';
		for (int k = 1; k <= 4; k++)
			if (k != j) cout << pos[k].second << ' ';
		cout << endl; read(res); if (res) return value;
		mark[value] = true, cnt++;
	}
	for (int i = 0; i <= 6; i++)
		if (!mark[i]) return i;
	return -1;
}
int gete(int x) { // Modulo 8
	int ans = odd[x];
	for (int i = 1; i <= 4; i++) {
		int value = (4 - (10 - i) % 4) % 4;
		if (value == ans) {
			cout << '?' << ' ' << 4 << ' ' << x << ' ';
			for (int k = 1; k <= 4; k++)
				if (k != i) cout << pos[k].first << ' ';
			cout << endl; read(res);
			if (res) ans = ans;
			else ans = ans + 2;
			break;
		}
	}
	for (int i = 1; i <= 4; i++) {
		int value = (8 - (n * 4 + 4 - i) % 8) % 8;
		if (value == ans || value == ans + 4) {
			cout << '?' << ' ' << 8 << ' ' << x << ' ';
			for (int k = 1; k <= 4; k++) {
				if (k != i) cout << pos[k].first << ' ';
				cout << pos[k].second << ' ';
			}
			cout << endl; read(res);
			if (res ^ (value == ans)) ans = ans + 4;
			else ans = ans;
			break;
		}
	}
	return ans;
}
void Output() {
	if (ans[1] > n / 2) {
		for (int i = 1; i <= n; i++)
			ans[i] = n + 1 - ans[i];
	}
	cout << '!' << ' ';
	for (int i = 1; i <= n; i++)
		cout << ans[i] << ' ';
	cout << endl;
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		cout << '?' << ' ' << n - 1 << ' ';
		for (int j = 1; j <= n; j++)
			if (i != j) cout << j << ' ';
		cout << endl; read(res);
		if (res) {
			if (pos[1].first) pos[1].second = i;
			else pos[1].first = i;
		}
	}
	used[pos[1].first] = used[pos[1].second] = true;
	odd[pos[1].first] = true;
	for (int i = 1; i <= n; i++)
		if (i != pos[1].first) {
			cout << '?' << ' ' << 2 << ' ' << i << ' ' << pos[1].first << endl;
			read(odd[i]);
		}
	for (int k = 2; k <= 4 && k * 2 <= n; k++) {
		for (int i = 1; i <= n; i++) {
			if (used[i]) continue;
			cout << '?' << ' ' << n - k * 2 + 1 << ' ';
			for (int j = 1; j <= n; j++)
				if (i != j && !used[j]) cout << j << ' ';
			cout << endl; read(res);
			if (res) {
				if (odd[i] ^ (k % 2 == 1)) pos[k].second = i;
				else pos[k].first = i;
			}
		}
		used[pos[k].first] = used[pos[k].second] = true;
	}
	for (int i = 1; i <= 4 && i <= n / 2; i++) {
		ans[pos[i].first] = i;
		ans[pos[i].second] = n + 1 - i;
	}
	if (n <= 6) {
		Output();
		return 0;
	}
	for (int i = 1; i <= n; i++) {
		if (ans[i]) continue;
		int t = gett(i);
		int f = getf(i);
		int s = gets(i);
		int e = gete(i);
		for (int j = 1; j <= n; j++)
			if (j % 3 == t && j % 5 == f && j % 7 == s && j % 8 == e) {
				ans[i] = j;
				break;
			}
	}
	Output();
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/104251950