【CodeChef】October Challenge 2018 (Div. 1 + Div. 2) 題解
阿新 • • 發佈:2018-11-19
【比賽連結】
**【BBRICKS】**Beautiful Bricks
【思路要點】
- 上下兩個磚塊中,至多有一個黑色。
- 連續的一段存在黑色的行共有兩種放置的方案。
- 列舉有幾段連續的存在黑色的行,用組合數計算答案。
- 單組資料時間複雜度 。
【程式碼】
#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
【思路要點】
- 每個時刻只可能存在一種角色,每 個時間單位數量翻倍。
- 直接計算答案即可。
- 時間複雜度 。
【程式碼】
#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
【思路要點】
- 列舉一對圓,能夠達到的距離是一個區間,算出該區間即可。
- 注意圓相互包含和外離的情況。
- 時間複雜度 。
【程式碼】
#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
【思路要點】
- 直接計算答案就好。
- 時間複雜度 。
【程式碼】
#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
【思路要點】
- 首先顯然所有形如 的數等價於 ,不妨先做這一步轉化。
- 有一種較為簡單的 的做法,用 記錄每一個質數作為質因數出現的位置,對於一個數 將其所有質因數的 或起來再取反,就可以得到它的邊集,簡單搜尋即可。
- 但這樣的做法複雜度太高,注意到 的質因數每個數只有一個,考慮將所有數按照最大的質因數分組。若 最大的質因數 ,那麼 會連向不是同組、且與 互質的數,通過一些細節處理可以在 的時間複雜度內進行等價的連邊。
- 剩餘的不存在 的質因數的數的個數不超過 。
- 時間複雜度 ,其中 。
【程式碼】
#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
【思路要點】
- 列舉子矩形的左邊界,將 行進行字尾排序,令結果為 。
- 從右向左依次考慮所有矩形的右邊界,初始時,我們認為全部的 行是不同的,此時的貢獻可以直接由組合數算出。每當右邊界達到了某兩個相鄰的 的 時,我們合併 和 ,重新計算此時的貢獻,我們需要減去包含與 相同的位置,不包含與 相同的位置 或是 不包含與 相同的位置,包含與 相同的位置的區間個數。
- 用並查集 配合啟發式合併可以完成上述功能。
- 時間複雜度 。
【程式碼】
#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 &