比賽鏈接
官方題解
Problem A. Nickname
輸出 即可。
時間複雜度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
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;
}
char s[MAXN];
int main() {
scanf("%s", s + 1), s[4] = 0;
printf("%s\n", s + 1);
return 0;
}
Problem B. Tag
判斷 與 的大小關係即可。
時間複雜度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
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 main() {
int a, v, b, w, t;
read(a), read(v), read(b), read(w), read(t);
int dist = abs(a - b);
if (v > w && 1ll * t * (v - w) >= dist) puts("YES");
else puts("NO");
return 0;
}
Problem C. Lamps
考慮初始時 的情況,則經過 次操作後,應當有 。
因此,有效操作的輪數不會超過 。模擬,直到 或是操作次數用盡即可。
時間複雜度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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, k, a[MAXN];
bool work() {
static int s[MAXN];
memset(s, 0, sizeof(s));
for (int i = 1; i <= n; i++) {
int l = max(1, i - a[i]), r = min(n, i + a[i]);
s[l]++, s[r + 1]--;
}
bool res = false;
for (int i = 1; i <= n; i++) {
s[i] += s[i - 1];
if (s[i] != a[i]) {
res = true;
a[i] = s[i];
}
}
return res;
}
int main() {
read(n), read(k);
for (int i = 1; i <= n; i++)
read(a[i]);
for (int i = 1; i <= k; i++)
if (!work()) break;
for (int i = 1; i <= n; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
Problem D. Knapsack Queries on a tree
一種可行的暴力是記 表示考慮節點 及其祖先,剩餘 容量的揹包的最優解。
則 顯然可以通過 的掃描由 轉移得到。
該做法的預處理複雜度爲 ,單組詢問的複雜度爲 。
另一種可行的暴力是直接枚舉選取物品的集合。
由於樹高不超過 ,該做法的預處理複雜度爲 ,單組詢問的複雜度爲 。
取 ,按照第一種方法計算 ,則可以 回答出現位置 的詢問。
對於剩餘詢問,枚舉 的物品是否選取,並用 值計算 的物品的最優選取方案即可。
時間複雜度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
const int MAXV = 1e5 + 5;
const int MAXM = 1 << 9;
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 a[MAXM][MAXV];
int n, m, r, q, v[MAXN], w[MAXN];
int query(int x, int y) {
if (x <= m) return a[x][y];
else {
int ans = query(x / 2, y);
if (y >= w[x]) chkmax(ans, v[x] + query(x / 2, y - w[x]));
return ans;
}
}
int main() {
read(n), m = (1 << 9) - 1, r = 1e5;
for (int i = 1; i <= n; i++)
read(v[i]), read(w[i]);
for (int i = 1; i <= m; i++) {
int f = i / 2;
for (int j = 1; j <= r; j++) {
a[i][j] = a[f][j];
if (j >= w[i]) chkmax(a[i][j], v[i] + a[f][j - w[i]]);
}
}
read(q);
for (int i = 1; i <= q; i++) {
int x, y; read(x), read(y);
printf("%d\n", query(x, y));
}
return 0;
}
Problem E. O(rand)
不妨認爲 ,且 。
則題目中的要求等價於:對於每一個二進制位,選出的集合既有 ,也有 。
考慮枚舉選擇的集合中第一個元素,則對於每個二進制位,已經存在了 中的一者。
剩餘問題可轉換爲:選擇一個集合,使得集合的二進制與的結果爲 ,求方案數。
則用 FWT 配合容斥原理解決轉化後的問題即可。
時間複雜度 。
官方題解中,給出了一種複雜度更爲優秀的解法。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const int MAXV = 1 << 18;
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;
}
ll coef[MAXN], binom[MAXN][MAXN];
int cnt[MAXV], bits[MAXV];
int m, goal, n, k, s, t, bit[MAXN], a[MAXN];
bool inc(int x, int y) {
return (x | y) == x;
}
void FWTOR(int *a, int N) {
for (int len = 2; len <= N; len <<= 1)
for (int s = 0; s < N; s += len)
for (int i = s, j = s + len / 2; j < s + len; i++, j++)
a[i] = a[i] + a[j];
}
int main() {
read(m), read(k), read(s), read(t);
if (!inc(t, s)) {
puts("0");
return 0;
}
for (int i = 1; i <= m; i++) {
int x; read(x);
if (inc(x, s) && inc(t, x)) a[++n] = x;
}
m = 0, goal = 0;
for (int i = 1; i <= 18; i++) {
bit[i] = 1 << (i - 1);
if ((t & bit[i]) && (s & bit[i]) == 0) m++, goal = goal * 2 + 1;
}
for (int i = 1; i <= n; i++) {
int res = 0;
for (int j = 1; j <= 18; j++)
if ((t & bit[j]) && (s & bit[j]) == 0) {
if (a[i] & bit[j]) res = res * 2 + 1;
else res = res * 2;
}
a[i] = res;
}
for (int i = 0; i <= n; i++) {
binom[i][0] = 1;
for (int j = 1; j <= i; j++)
binom[i][j] = binom[i - 1][j - 1] + binom[i - 1][j];
for (int j = 0; j <= k - 1; j++)
coef[i] += binom[i][j];
}
for (int i = 1; i <= goal; i++)
bits[i] = bits[i / 2] + i % 2;
ll ans = 0;
for (int i = 1; i <= n; i++) {
memset(cnt, 0, sizeof(cnt));
for (int j = i + 1; j <= n; j++)
cnt[a[i] ^ a[j] ^ goal]++;
FWTOR(cnt, goal + 1);
for (int j = 0; j <= goal; j++)
if (bits[j] & 1) ans -= coef[cnt[j]];
else ans += coef[cnt[j]];
}
cout << ans << endl;
return 0;
}
Problem F. Triangles
不失一般性地,考慮頂點在 處的三角形。
由對稱性,考慮僅計算 的情況,令 。
由畢克定理,三角形內部的點數 ,邊界上的點數 ,面積 應當滿足
也即
整理得
因此,我們需要計算滿足如下不等式的 的個數:
考慮枚舉 則不等式的右側應當爲一個定值。
由於 ,即不等式左側關於 單調。
因此,我們可以 算出滿足不等式的 的個數。
又因爲使得不等式右側 的 的組數不超過 ,枚舉即可。
時間複雜度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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, m, k;
ll work(int n, int m) {
ll ans = 0;
for (int i = 0; i <= n - 2; i++)
for (int j = 1; j <= m - 1 && i * j <= 2 * k + m; j++) {
int tmp = 2 * k + __gcd(i, m) - i * j - 2;
if (tmp >= 0) {
int limit = min(n - 1 - i, tmp / m + 1);
ans += (limit - (m * limit - __gcd(limit, j) - __gcd(limit + i, m - j) > tmp)) * (1 + (i != 0));
}
}
return ans;
}
int main() {
read(n), read(m), read(k);
cout << work(n, m) * 2 + work(m, n) * 2 << endl;
return 0;
}