1. 程式人生 > >【CodeForces】Avito Code Challenge 2018 (Div. 1 + Div. 2) 題解

【CodeForces】Avito Code Challenge 2018 (Div. 1 + Div. 2) 題解

【比賽連結】

【題解連結】

**【A】**Antipalindrome

【思路要點】

  • 當所有字元相同,答案為 0 0
  • 否則,若原串為迴文串,刪去其最後一個字元一定會使其變成非迴文串,因此答案為 N
    1 N-1
    ,否則答案為 N N
  • 時間複雜度 O
    ( N ) O(N)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
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; char s[MAXN];
bool check() {
	for (int i = 1, j = n; i <= j; i++, j--)
		if (s[i] != s[j]) return false;
	return true;
}
int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	bool flg = true;
	for (int i = 1; i <= n; i++)
		flg &= s[i] == s[1];
	if (flg) printf("%d\n", 0);
	else if (check()) printf("%d\n", n - 1);
	else printf("%d\n", n);
	return 0;
}

**【B】**Businessmen Problems

【思路要點】

  • s t d : : m a p std::map 實現取每一種展品的最大值。
  • 時間複雜度 O ( N L o g N ) O(NLogN)

【程式碼】

#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("");
}
map <int, int> mp;
int main() {
	ll ans = 0;
	int n; read(n);
	for (int i = 1; i <= n; i++) {
		int x, y; read(x), read(y);
		ans += y; mp[x] = y;
	}
	int m; read(m);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		ans += y; ans -= min(mp[x], y);
	}
	writeln(ans);
	return 0;
}

**【C】**Useful Decomposition

【思路要點】

  • 唯一可能的合法情況是所有路徑均交於一點。
  • 取度數最大的點作為該點, d f s dfs 構造路徑即可。
  • 時間複雜度 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, root;
vector <int> a[MAXN];
int dfs(int pos, int fa) {
	int dest = 0;
	for (auto x : a[pos])
		if (x != fa) {
			if (dest) {
				printf("No\n");
				exit(0);
			} else dest = x;
		}
	if (dest == 0) return pos;
	else return dfs(dest, pos);
}
int main() {
	read(n);
	for (int i = 1; i <= n - 1; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	root = 1;
	for (int i = 2; i <= n; i++)
		if (a[i].size() > a[root].size()) root = i;
	vector <int> ans;
	for (auto x : a[root])
		ans.push_back(dfs(x, root));
	printf("Yes\n");
	writeln(ans.size());
	for (auto x : ans)
		printf("%d %d\n", root, x);
	return 0;
}

**【D】**Bookshelves

【思路要點】

  • 逐位確定答案。
  • 對於答案 a n s ans ,我們需要判斷是否能將序列分成區間和 s u m i sum_i 滿足 s u m i &amp; a n s = a n s sum_i\&amp;ans=ans k k 段,可以用動態規劃判斷。
  • d p i , j dp_{i,j} 表示能否將序列的前 i i 個元素劃分成 j j 個合法區間,列舉下一個區間的右端點進行轉移。
  • 時間複雜度 O ( N 2 K L o g V ) O(N^2KLogV)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
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;
ll ans, s[MAXN], a[MAXN];
bool check(ll ans) {
	static bool dp[MAXN][MAXN];
	memset(dp, false, sizeof(dp));
	dp[0][0] = true;
	for (int i = 0; i <= n - 1; i++)
	for (int j = 0; j <= k - 1; j++)
		if (dp[i][j]) {
			for (int p = i + 1; p <= n; p++)
				if (((s[p] - s[i]) & ans) == ans) dp[p][j + 1] = true;
		}
	return dp[n][k];
}
int main() {
	read(n), read(k);
	for (int i = 1; i <= n; i++)
		read(a[i]), s[i] = s[i - 1] + a[i];
	for (int i = 60; i >= 0; i--) {
		ans += 1ll << i;
		if (!check(ans)) ans -= 1ll << i;
	}
	writeln(ans);
	return 0;
}

**【E】**Addition on Segments

【思路要點】

  • 若我們確定了區間最大值的位置,顯然我們可以選擇不進行不包含這個位置的操作來確保其成為區間最大值。
  • 因此,我們希望計算出包含每個位置的詢問權值的揹包,再合併得到答案。
  • 注意到如果用 b i t s e t bitset 來儲存揹包,我們可以在 O ( N w ) O(\frac{N}{w}) 的時間內加入一個權值,但是不方便刪除。
  • 因此,我們可以採用線段樹分治的思想,將一個詢問拆分為 O ( L o g N ) O(LogN) 個詢問,放線上段樹上,這樣,從根節點 d f s dfs 到某個葉子節點的路徑上的所有詢問即為包含該位置的詢問,據此進行轉移即可。
  • 時間複雜度 O ( N Q L o g N w ) O(\frac{NQLogN}{w})

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e4 + 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 SegmentTree {
	struct Node {
		int lc, rc;
		vector <int> tag;
	} a[MAXN * 2];
	int n, size, root;
	bitset <MAXN> ans;
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void modify(int root, int l, int r, int ql, int qr, int x) {
		if (l == ql && r == qr) {
			a[root].tag.push_back(x);
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= ql) modify(a[root].lc, l, mid, ql, min(qr, mid), x);
		if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
	}
	void modify(int l, int r, int x) {
		if (l > r) return;
		else modify(root, 1, n, l, r, x);
	}
	void work(int pos, bitset <MAXN> now) {
		for (auto x : a[pos].tag)
			now |= now << x;
		if (a[pos].lc == 0) {
			ans |= now;
			return;
		}
		work(a[pos].lc, now);
		work(a[pos].rc, now);
	}
	void getans() {
		work(root, 1);
		vector <int> finalans;
		for (int i = 1; i <= n; i++)
			if (ans[i]) finalans.push_back(i);
		writeln(finalans.size());
		for (auto x : finalans)
			printf("%d ", x);
	}
} ST;
int n, m;
int main() {
	read(n), read(m);
	ST.init(n);
	for (int i = 1; i <= m; i++) {
		int l, r, x;
		read(l), read(r), read(x);
		ST.modify(l, r, x);
	}
	ST.getans();
	return 0;
}

**【F】**Round Marriage

【思路要點】

  • 首先二分答案,現在我們只需要判斷某個答案 m i d mid 是否合法。
  • 不妨令 a 1 a 2 a 3 . . . a N , b 1 b 2 b 3 . . . b N a_1≤a_2≤a_3≤...≤a_N,b_1≤b_2≤b_3≤...≤b_N ,一旦確定 a 1 a_1 對應的 b i b_i ,最優的對應方案就一定是 a 1 b i , a 2 b i + 1 , . . . , a N b i 1 a_1-b_i,a_2-b_{i+1},...,a_N-b_{i-1} ,否則,將答案調整至這樣不會變劣。
  • 每一個 a i a_i 都有一個能夠對應的 b i b_i 區間,每一個區間都會對 a 1 a_1 可能的對應位置作出限制,考慮完所有 a i a_i 都判斷 a 1 a_1 可能的對應位置是否為空即可。
  • 時間複雜度 O ( N L o g V ) O(NLogV)

【程式碼】

#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, len;
int a[MAXN], b[MAXN];
int calc(int pos) {
	int ans = 0;
	if (pos < 1) pos += n, ans -= len;
	if (pos > n) pos -= n, ans += len;
	ans += b[pos];
	return ans;
}
bool check(int mid) {
	int ql = -n, qr = n;
	for (int i = 1; i <= n; i++) {
		while (qr >= ql && abs(a[i] - calc(i + ql)) > mid) ql++;
		while (qr >= ql && abs(a[i] - calc(i + qr)) > mid) qr--;
		if (qr < ql) return false;
	}
	return true;
}
int main() {
	read(n), read(len);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	for (int i = 1; i <= n; i++)
		read(b[i]);
	sort(a + 1, a + n + 1);
	sort(b + 1, b + n + 1);
	int l = 0, r = len;
	while (l < r) {
		int mid = (l + r) / 2;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	writeln(l);
	return 0;
}

**【G】**Magic multisets

【思路要點】