1. 程式人生 > >【CodeForces】CodeForces Round #517 (Div. 1 + Div. 2) 題解

【CodeForces】CodeForces Round #517 (Div. 1 + Div. 2) 題解

【比賽連結】

【題解連結】

**【Div.2 A】**Golden Plate

【思路要點】

  • 直接迴圈計算答案即可。
  • 時間複雜度 O ( K )
    O(K)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 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 main() {
	int n, m, k;
	read(n), read(m), read(k);
	int ans = 0;
	for (int i = 1; i <= k; i++) {
		int x = n - 4 * (i - 1);
		int y = m - 4 * (i - 1);
		ans += x * 2 + y * 2 - 4;
	}
	writeln(ans);
	return 0;
}

**【Div.2 B】**Curiosity Has No Limits

【思路要點】

  • 首先,每一位的方案是相互獨立的,我們可以分開考慮。
  • 當確定 x , x &amp; y
    , x y x,x\&amp;y,x|y
    時,合法的 y y 至多隻有 1 1 個。
  • 因此列舉開頭位置,構造出序列,再檢驗合法性即可。
  • 時間複雜度 O ( N ) O(N)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 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 n, a[MAXN], b[MAXN], x[MAXN];
int func(int a, int b, int x) {
	if (a == b) return a;
	else if (a == 0) return 0;
	else return 1 - x;
}
bool tryans(int f) {
	x[1] = f;
	for (int i = 1; i <= n - 1; i++)
		x[i + 1] = func(a[i] / 2, b[i] / 2, x[i] / 2) * 2 + func(a[i] % 2, b[i] % 2, x[i] % 2);
	for (int i = 1; i <= n - 1; i++) {
		if ((x[i] | x[i + 1]) != a[i]) return false;
		if ((x[i] & x[i + 1]) != b[i]) return false;
	}
	return true;
}
void answer() {
	printf("YES\n");
	for (int i = 1; i <= n; i++)
		printf("%d ", x[i]);
	exit(0);
}
int main() {
	read(n);
	for (int i = 1; i <= n - 1; i++)
		read(a[i]);
	for (int i = 1; i <= n - 1; i++)
		read(b[i]);
	if (tryans(0)) answer();
	if (tryans(1)) answer();
	if (tryans(2)) answer();
	if (tryans(3)) answer();
	printf("NO\n");
	return 0;
}

**【Div.2 C/Div.1 A】**Cram Time

【思路要點】

  • x x 表示最大的使得 x ( x + 1 ) 2 a + b \frac{x(x+1)}{2}≤a+b 的整數,答案顯然有上界為 x x
  • 若我們保證第一天用完剛好 a a 小時或完成所有 x x 個任務,那麼答案將一定會取到上界。只需要從大到小考慮每個任務,優先將其安排在第一天即可。
  • 時間複雜度 O ( a + b ) O(\sqrt{a+b})

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 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("");
}
vector <int> a, b;
int main() {
	int n, m; read(n), read(m);
	int sum = n + m, tot = 0;
	while (sum >= tot + 1) {
		tot++;
		sum -= tot;
	}
	for (int i = tot; i >= 1; i--)
		if (n >= i) {
			n -= i;
			a.push_back(i);
		} else b.push_back(i);
	writeln(a.size());
	for (auto x : a)
		printf("%d ", x);
	printf("\n");
	writeln(b.size());
	for (auto x : b)
		printf("%d ", x);
	printf("\n");
	return 0;
}

**【Div.2 D/Div.1 B】**Minimum path

【思路要點】

  • 分層 D P DP ,令點 ( i , j ) (i,j) 為第 i + j 1 i+j-1 層的點。
  • 首先確定答案的第 i i a n s i ans_i ,再計算得出能夠取到最優答案的第 i i 層的結尾位置以及這些位置最多能夠剩餘的修改次數,層層遞進即可。
  • 時間複雜度 O ( N 2 ) O(N^2)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e3 + 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 n, k, dp[MAXN][MAXN];
char s[MAXN][MAXN], ans[MAXN * 2];
int main() {
	read(n), read(k);
	for (int i = 1; i <= n; i++)
		scanf("\n%s", s[i] + 1);
	memset(dp, -1, sizeof(dp));
	if (s[1][1] == 'a' || k >= 1) {
		ans[1] = 'a';
		dp[1][1] = k - (s[1][1] != 'a');
	} else {
		ans[1] = s[1][1];
		dp[1][1] = 0;
	}
	for (int i = 2, now = 0, dest = 1; i <= 2 * n - 1; i++, swap(now, dest)) {
		char opt = 'z';
		for (int x = 1; x <= n; x++) {
			int y = i - x;
			if (y < 0 || y > n || dp[x][y] == -1) continue;
			if (dp[x][y]) opt = 'a';
			if (x + 1 <= n) chkmin(opt, s[x + 1][y]);

			if (y + 1 <= n) chkmin(opt, s[x][y + 1]);
		}
		ans[i] = opt;
		for (int x = 1; x <= n; x++) {
			int y = i - x;
			if (y < 0 || y > n || dp[x][y] == -1) continue;
			if (dp[x][y]) {
				if (x + 1 <= n) chkmax(dp[x + 1][y], dp[x][y] - (s[x + 1][y] != 'a'));
				if (y + 1 <= n) chkmax(dp[x][y + 1], dp[x][y] - (s[x][y + 1] != 'a'));
			} else {
				if (x + 1 <= n && s[x + 1][y] == opt) chkmax(dp[x + 1][y], dp[x][y]);
				if (y + 1 <= n && s[x][y + 1] == opt) chkmax(dp[x][y + 1], dp[x][y]);
			}
		}
	}
	printf("%s\n", ans + 1);
	return 0;
}

**【Div.2 E/Div.1 C】**Triple Flips

【思路要點】

  • 寫一個暴力,事實表明當 N 8 N≥8 ,問題將始終有解,並且 N = 12 N=12 的所有情況均存在一個 5 5 步內的解。
  • 接下來我們的思路是通過大約 N 3 \frac{N}{3} 次操作將序列包含 1 1 的長度減小至 12 12 ,並通過暴力解決剩餘的問題。
  • 由於能夠進行的運算元只有大約 N 3 \frac{N}{3} 次,我們需要保證在 1 1 步內將序列包含 1 1 的長度減小 3 3 ,或是在 2 2 步內將序列包含 1 1 的長度減小 6 6
  • 以下是筆者的做法:
    1 1 、判斷當前區間開頭 3 3 個元素是否均為 1 1 ,若是,用一次操作處理之,並刪去區間開頭 3 3 個元素;區間結尾 3 3 個元素同理。
    2 2 、現在我們可以認為區間開頭、結尾 3 3 個元素均至多有 2 2 1 1 。若區間開頭 3 3 個元素只有 1 1 1 1 ,用一次操作處理之,並刪去區間開頭 3 3 個元素,區間結尾 3 3 個元素同理。
    3 3 、現在我們可以認為區間開頭、結尾 3 3 個元素均恰好有 2 2 1 1 。若區間開頭 3 3 個元素形如 1 , 0 , 1 1,0,1 ,用一次操作處理之,並刪去區間開頭 3 3 個元素,區間結尾 3 3 個元素同理。
    4 4 、剩餘的唯一情況是區間開頭 3 3 個元素形如 1 , 1 , 0 1,1,0 ,區間結尾 3 3 個元素形如 0 , 1 , 1 0,1,1 。可用兩次操作處理之,並刪去區間開頭、結尾的 3 3 個元素。
  • 如此迭代刪除,直至區間長度降至恰好 12 12
  • 時間複雜度 O ( N + 2 12 1 2 2 ) O(N+2^{12}*12^2) ,使用操作次數不超過 N 3 + 5 \lfloor\frac{N}{3}\rfloor+5

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 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("");
}
vector <int> x, y, z;
namespace force {
	const int MAXN = 13;
	unordered_map <bitset <MAXN>, bitset <MAXN> > from;
	bitset <MAXN> q[1 << MAXN], dest;
	void work(int *a, int n, int delta) {
		dest.reset();
		for (int i = 1; i <= n; i++)
			dest[i] = a[i];
		if (dest == 0) return;
		int l = 0, r = 0;
		q[0] = 0, from[0] = 0;
		while (l <= r) {
			bitset <MAXN> now = q[l++];
			for (int i = 1; i <= n; i++)
			for (int j = i + 2; j <= n; j += 2) {
				int k = (i + j) / 2;
				bitset <MAXN> tmp = now;
				tmp.flip(i), tmp.flip(j), tmp.flip(k);
				if (from.count(tmp) == 0) {
					from[tmp] = now;
					q[++r] = tmp;
					if (tmp == dest) {
						bitset <MAXN> pos = tmp;
						while (pos != 0) {
							bitset <MAXN> tnp = pos ^ from[pos];
							int val = 0;
							x.push_back((val = tnp._Find_first()) + delta);
							y.push_back((val = tnp._Find_next(val)) + delta);
							z.push_back((val = tnp._Find_next(val)) + delta);
							pos = from[pos];
						}
						return;
					}
				}
			}
		}
		printf("NO\n");
		exit(0);
	}
}
int n, a[MAXN];
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	if (n <= 12) force :: work(a, n, 0);
	else {
		int head = 1, back = n;
		while (back - head >= 12 && a[head] == 0) head++;
		while (back - head >= 12 && a[back] == 0) back--;
		while (back - head >= 12) {
			if (a[head] + a[head + 1] + a[head + 2] == 3) {
				a[head] = a[head + 1] = a[head + 2] = 0;
				x.push_back(head);
				y.push_back(head + 1);
				z.push_back(head + 2);
			} else if (a[back] + a[back - 1] + a[back - 2] == 3) {
				a[back] = a[back - 1] = a[back - 2] = 0;
				x.push_back(back);
				y.push_back(back - 1);
				z.push_back(back - 2);
			} else if ((head + back) % 2 == 0) {
				a[head] = a[back] = 0;
				int mid = (head + back) / 2;
				a[mid] = 1 - a[mid];
				x.push_back(head);
				y.push_back(back);
				z.push_back(mid);
			} else if (a[head + 1] == 1) {
				a[head + 1] = a[back] = 0;
				int mid = (head + 1 + back) / 2;
				a[mid] = 1 - a[mid];
				x.push_back(head + 1);
				y.push_back(back);
				z.push_back(mid);
			} else if (a[back - 1] == 1) {
				a[head] = a[back - 1] = 0;
				int mid = (head + back - 1) / 2;
				a[mid] = 1 - a[mid];
				x.push_back(head);
				y.push_back(back - 1);
				z.push_back(mid);
			} else if (a[head + 2] == 1) {
				a[head + 2] = a[head] = 0;
				a[head + 4] = 1 - a[head + 4];
				x.push_back(head);
				y.push_back(head + 2);
				z.push_back(head + 4);
			} else if (a[back - 2] == 1) {
				a[back - 2] = a[back] = 0;
				a[back - 4] = 1 - a[back - 4];
				x.push_back(back);
				y.push_back(back - 2);
				z.push_back(back - 4);
			} else {
				int tmp = back - 3;
				a[head] = 1 - a[head];
				a[tmp] = 1 - a[tmp];
				int mid = (head + tmp) / 2;
				a[mid] = 1 - a[mid];
				x.push_back(head);
				y.push_back(tmp);
				z.push_back(mid);
			}
			while (back - head >= 12 && a[head] == 0) head++;
			while (back - head >= 12 && a[back] == 0) back--;
		}
		force :: work(a + head - 1, back - head + 1, head - 1);
	}
	printf("YES\n");
	assert((int) x.size() <= n / 3 + 12);
	writeln(x.size());
	for (unsigned i = 0; i < x.size(); i++)
		printf("%d %d %d\n", x[i], y[i], z[i]);
	return 0;
}

**【Div.2 F/Div.1 D】**Familiar Operations

【思路要點】