1. 程式人生 > >【CodeChef】October Challenge 2018 (Div. 1 + Div. 2) 題解

【CodeChef】October Challenge 2018 (Div. 1 + Div. 2) 題解

【比賽連結】

**【BBRICKS】**Beautiful Bricks

【思路要點】

  • 上下兩個磚塊中,至多有一個黑色。
  • 連續的一段存在黑色的行共有兩種放置的方案。
  • 列舉有幾段連續的存在黑色的行,用組合數計算答案。
  • 單組資料時間複雜度 O ( K
    ) O(K)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 1e9 + 7;
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, bit[MAXN], fac[MAXN], inv[MAXN];
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
int getc(int x, int y) {
	if (y > x) return 0;
	else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
int main() {
	k = 1e3;
	fac[0] = 1;
	for (int i = 1; i <= k; i++)
		fac[i] = 1ll * fac[i - 1] * i % P;
	inv[k] = power(fac[k], P - 2);
	for (int i = k - 1; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1ll) % P;
	bit[0] = 1;
	for (int i = 1; i <= k; i++)
		bit[i] = bit[i - 1] * 2 % P;
	int T; read(T);
	while (T--) {
		read(n), read(k);
		int tmp = n - k + 1;
		int ans = 0, now = 1;
		if (tmp < 0) now = 0;
		for (int i = 1; i <= k; i++) {
			now = 1ll * now * (tmp - i + 1) % P;
			ans = (ans + 1ll * getc(k - 1, i - 1) * now % P * inv[i] % P * bit[i]) % P;
		}
		writeln(ans);
	}
	return 0;
}

**【BITOBYT】**Byte to Bit

【思路要點】

  • 每個時刻只可能存在一種角色,每 26 26 個時間單位數量翻倍。
  • 直接計算答案即可。
  • 時間複雜度 O (
    T L o g N ) O(TLogN)

【程式碼】

#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("");
}
ll power(int x, int y) {
	if (y == 0) return 1;
	ll tmp = power(x, y / 2);
	if (y % 2 == 0) return tmp * tmp;
	else return tmp * tmp * x;
}
int main() {
	int T; read(T);
	while (T--) {
		int n; read(n);
		int q = n / 26, r = n % 26;
		if (r == 0) q--, r = 26;
		ll x = 0, y = 0, z = 0, ans = power(2, q);
		if (r <= 2) x = ans;
		else if (r <= 10) y = ans;
		else z = ans;
		printf("%lld %lld %lld\n", x, y, z);
	}
	return 0;
}

**【CCIRCLES】**Chef and Circles

【思路要點】

  • 列舉一對圓,能夠達到的距離是一個區間,算出該區間即可。
  • 注意圓相互包含和外離的情況。
  • 時間複雜度 O ( K + N 2 ) O(K+N^2)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 5;
const int MAXV = 1e6 + 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, q, r[MAXN], ans[MAXV];
struct point {int x, y; } a[MAXN];
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
long long dist(point a) {return 1ll * a.x * a.x + 1ll * a.y * a.y; }
int main() {
	read(n), read(q);
	for (int i = 1; i <= n; i++)
		read(a[i].x), read(a[i].y), read(r[i]);
	for (int i = 1; i <= n; i++)
	for (int j = i + 1; j <= n; j++) {
		ll tmp = dist(a[i] - a[j]);
		int Max = sqrt(tmp);
		int Min = sqrt(tmp);
		if (1ll * Min * Min < tmp) Min++;
		if (Min - r[i] - r[j] >= 0) Min = Min - r[i] - r[j];
		else Min = max(0, abs(r[i] - r[j]) - Max);
		Max = min(1000000, Max + r[i] + r[j]);
		ans[Min]++, ans[Max + 1]--; 
	}
	for (int i = 1; i < MAXV; i++)
		ans[i] += ans[i - 1];
	while (q--) {
		int x; read(x);
		printf("%d\n", ans[x]);
	}
	return 0;
}

**【CHSERVE】**Chef and Serves

【思路要點】

  • 直接計算答案就好。
  • 時間複雜度 O ( T ) O(T)

【程式碼】

#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 T; read(T);
	while (T--) {
		int x, y, k;
		read(x), read(y), read(k);
		int tmp = (x + y + 1) % (2 * k);
		if (tmp == 0) tmp = 2 * k;
		if (tmp <= k) printf("CHEF\n");
		else printf("COOK\n");
	}
	return 0;
}

**【CPCOMP】**Coprime Components

【思路要點】

  • 首先顯然所有形如 x a y b . . . z c x^ay^b...z^c 的數等價於 x y . . . z xy...z ,不妨先做這一步轉化。
  • 有一種較為簡單的 O ( N 2 L o g L o g V w ) O(\frac{N^2*LogLogV}{w}) 的做法,用 b i t s e t bitset 記錄每一個質數作為質因數出現的位置,對於一個數 x x 將其所有質因數的 b i t s e t bitset 或起來再取反,就可以得到它的邊集,簡單搜尋即可。
  • 但這樣的做法複雜度太高,注意到 &gt; V &gt;\sqrt{V} 的質因數每個數只有一個,考慮將所有數按照最大的質因數分組。若 x x 最大的質因數 p &gt; V p&gt;\sqrt{V} ,那麼 x x 會連向不是同組、且與 x p \frac{x}{p} 互質的數,通過一些細節處理可以在 O ( N V ) O(N\sqrt{V}) 的時間複雜度內進行等價的連邊。
  • 剩餘的不存在 &gt; V &gt;\sqrt{V} 的質因數的數的個數不超過 3 1 0 4 3*10^4
  • 時間複雜度 O ( N V + M 2 L o g L o g V w ) O(N\sqrt{V}+\frac{M^2*LogLogV}{w}) ,其中 M 3 1 0 4 M\leq 3*10^4

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 100;
const int Q = 3e4;
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 tot, prime[MAXN], miu[MAXN];
int f[MAXN], g[MAXN], realn[MAXN], num[MAXN];
void init(int n) {
	realn[1] = miu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) {
			prime[++tot] = f[i] = realn[i] = i;
			num[i] = tot;
			miu[i] = -1;
		}
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			if (prime[j] == f[i]) realn[tmp] = realn[i], miu[tmp] = 0;
			else realn[tmp] = realn[i] * prime[j], miu[tmp] = -miu[i];
			f[tmp] = prime[j];
		}
	}
	for (int i = 2; i <= n; i++) {
		int tmp = i;
		while (f[tmp] != tmp) tmp /= f[tmp];
		g[i] = tmp;
	}
}
int fa[MAXN];
int par(int x) {
	while (x != fa[x])
		x = fa[x] = fa[fa[x]];
	return x;
}
int main() {
	int n; read(n);
	vector <int> a(n + 1);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	sort(a.begin(), a.end());
	if (a[1] == 1) {
		printf("%d\n", 1);
		return 0;
	}
	int Max = a.back();
	init(Max);
	for (int i = 1; i <= n; i++)
		a[i] = realn[a[i]];
	sort(a.begin(), a.end());
	Max = a.back();
	static vector <int> factors[MAXN];
	for (int i = 1; i <= Max; i++)
	for (int j = i; j <= Max; j += i)
		factors[j].push_back(i);
	static int cnt[MAXN];
	for (int i = 1; i <= n; i++)
	for (auto j : factors[a[i]])
		cnt[j]++;
	int ans = 0;
	vector <int> b;
	for (int i = 1; i <= n; i++) {
		int tmp = 0;
		for (auto j : factors[a[i]])
			tmp += cnt[j] * miu[j];
		if (tmp == 0) ans++;
		else b.push_back(a[i]);
	}
	b.erase(unique(b.begin(), b.end()), b.end());
	a = b, n = a.size();
	int oldn = n;
	for (int i = 0; i < n; i++)
		fa[i] = i;
	int limit = sqrt(a.back());
	if (limit * limit <= a.back()) limit++;
	static int gcd[448][448];
	for (int i = 0; i < limit; i++)
	for (int j = 0; j < limit; j++)
		gcd[i][j] = __gcd(i, j);
	sort(a.begin(), a.end(), [&] (int x, int y) {return g[x] < g[y]; });
	static deque <pair <int, int> > p[MAXN];
	vector <int> used;
	for (int i = 0; i < n; i++)
		if (g[a[i]] >= limit) {
			p[a[i] / g[a[i]]].push_back(make_pair(g[a[i]], i));
			if (p[a[i] / g[a[i]]].size() == 1) used.push_back(a[i] / g[a[i]]);
		}
	for (int i = 0; i < n; i++)
	for (auto j : used)
		if (gcd[a[i] % j][j] == 1) {
			pair <int, int> tmp = make_pair(0, -1);
			while (!p[j].empty() && p[j].front().first != g[a[i]]) {
				tmp = p[j].front();
				fa[par(tmp.second)] = par(i);
				p[j].pop_front();
			}
			while (!p[j].empty() && p[j].back().first != g[a[i]]) {
				tmp = p[j].back();
				fa[par(tmp.second)] = par(i);
				p[j].pop_back();
			}
			if (tmp.second != -1) p[j].push_back(tmp);
		}
	while (!a.empty() && g[a.back()] >= limit) a.pop_back();
	n = a.size(); static bitset <Q> edge[P], vis;
	for (int i = 0; i < n; i++) {
		vis.set(i);
		int tmp = a[i];
		while (tmp != 1) {
			edge[num[f[tmp]]].set(i);
			tmp /= f[tmp];
		}
	}
	for (int i = 0; i < n; i++)
		if (vis[i]) {
			int l = 0, r = 0;
			static int q[MAXN];
			q[0] = i, vis[i] = false;
			while (l <= r) {
				int pos = q[l++];
				bitset <Q> e = vis;
				int tmp = a[pos];
				while (tmp != 1) {
					e &= ~edge[num[f[tmp]]];
					tmp /= f[tmp];
				}
				for (unsigned j = e._Find_first(); j < e.size(); j = e._Find_next(j)) {
					q[++r] = j, vis[j] = false;
					fa[par(pos)] = par(j);
				}
			}
		}
	for (int i = 0; i < oldn; i++)
		ans += par(i) == i;
	printf("%d\n", ans);
	return 0;
}

**【DISTRING】**Distinct Rows in Submatrices

【思路要點】

  • 列舉子矩形的左邊界,將 N N 行進行字尾排序,令結果為 p 1 , p 2 , . . . , p N p_1,p_2,...,p_N
  • 從右向左依次考慮所有矩形的右邊界,初始時,我們認為全部的 N N 行是不同的,此時的貢獻可以直接由組合數算出。每當右邊界達到了某兩個相鄰的 p i , p i + 1 p_i,p_{i+1} l c p lcp 時,我們合併 p i p_i p i + 1 p_{i+1} ,重新計算此時的貢獻,我們需要減去包含與 p i p_i 相同的位置,不包含與 p i + 1 p_{i+1} 相同的位置 或是 不包含與 p i p_i 相同的位置,包含與 p i + 1 p_{i+1} 相同的位置的區間個數。
  • 用並查集 + + s t d : : s e t std::set 配合啟發式合併可以完成上述功能。
  • 時間複雜度 O ( N M L o g N M + N M L o g 2 N ) O(NMLogNM+NMLog^2N)

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 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("");
}
namespace SuffixArray {
	const int MAXN = 5e5 + 5;
	const int MAXLOG = 22;
	const int MAXC = 5e5; 
	int sa[MAXN], rnk[MAXN], height[MAXN];
	int Min[MAXN][MAXLOG], bit[MAXN], N;
	void init(int *a, int n) {
		N = n;
		static int x[MAXN], y[MAXN], cnt[MAXN], rk[MAXN];
		memset(cnt, 0, sizeof(cnt));
		for (int i = 1; i <= n; i++)
			cnt[a[i]]++;
		for (int i = 1; i <= n; i++)
			cnt[i] += cnt[i - 1];
		for (int i = n; i >= 1; i--)
			sa[cnt[a[i]]--] = i;
		rnk[sa[1]] = 1;
		for (int i = 2; i <= n; i++)
			rnk[sa[i]] = rnk[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]]);
		for (int k = 1; rnk[sa[n]] != n; k <<= 1) {
			for (int i = 1; i <= n; i++) {
				x[i] = rnk[i];
				y[i] = (i + k <= n) ? rnk[i + k] : 0;
			}
			memset(cnt, 0, sizeof(cnt));
			for (int i = 1; i <= n; i++)
				cnt[y[i]]++;
			for (int i = 1; i <= n; i++)
				cnt[i] += cnt[i - 1];
			for (int i = n; i >= 1; i--)
				rk[cnt[y[i]]--] = i;
			memset(cnt, 0, sizeof(cnt));
			for (int i = 1; i &