【比赛链接】
【题解链接】
**【A】**Bear and Different Names
【思路要点】
首先生成 个不同的合法名字。
按照如下方式构造一组解:
保证前 个名字互不相同。
若 ,令 。
否则,令 为一个尚未使用的名字。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
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("");
}
char name[MAXN][5];
int n, k, ans[MAXN];
int main() {
read(n), read(k);
for (int i = 1; i <= n; i++) {
name[i][0] = 'A' + i / 26;
name[i][1] = 'a' + i % 26;
if (i < k) ans[i] = i;
else {
char opt[5];
scanf("\n%s", opt);
if (opt[0] == 'Y') ans[i] = i;
else ans[i] = ans[i - k + 1];
}
}
for (int i = 1; i <= n; i++)
printf("%s ", name[ans[i]]);
return 0;
}
**【B】**Bear and Tree Jumps
【思路要点】
通过树形 求得:
表示 的子树内与 距离模 余 的点数。
表示 的子树内的点走到 的步数总和。
再通过树形 的换根求得每个点作为根节点时的 和 即可。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int MAXK = 5;
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[MAXN];
int n, k;
struct info {long long sum; int cnt[MAXK]; };
info operator + (info a, info b) {
a.sum += b.sum;
for (int i = 0; i < k; i++)
a.cnt[i] += b.cnt[i];
return a;
}
info operator - (info a, info b) {
a.sum -= b.sum;
for (int i = 0; i < k; i++)
a.cnt[i] -= b.cnt[i];
return a;
}
info vary(info a) {
a.sum += a.cnt[0];
int tmp = a.cnt[0];
for (int i = 0; i < k - 1; i++)
a.cnt[i] = a.cnt[i + 1];
a.cnt[k - 1] = tmp;
return a;
}
info dp[MAXN];
long long ans;
void dfs(int pos, int fa) {
dp[pos].sum = 0;
dp[pos].cnt[0] = 1;
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i] != fa) {
dfs(a[pos][i], pos);
dp[pos] = dp[pos] + vary(dp[a[pos][i]]);
}
}
void work(int pos, int fa, info tmp) {
tmp = dp[pos] + tmp;
ans += tmp.sum;
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i] != fa) work(a[pos][i], pos, vary(tmp - vary(dp[a[pos][i]])));
}
int main() {
read(n), read(k);
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);
}
dfs(1, 0);
work(1, 0, (info) {0, {0}});
writeln(ans / 2);
return 0;
}
**【C】**Bear and Company
【思路要点】
记 表示已经放置了 个 , 个 , 个其他字符,并且上一个放置的字符是/不是 时,达到当前状态需要的最小移动步数。
最小移动步数不仅需要考虑一个字符的前后坐标之差,也需要考虑若两个同向的字符 的前后位置 若满足 或 ,也会对最小移动步数产生 的贡献。
考虑在 处的转移统计上述贡献,由于此时 均为已知量,我们只需要扫一遍整个字符串即可统计额外的贡献。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int INF = 1e9;
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("");
}
char s[MAXN];
int n, a[MAXN], cnt[3], pos[3][MAXN];
int dp[MAXN][MAXN][MAXN][2];
int cost(int type, int zero, int one, int two, int to, int from) {
int ans = abs(from - to);
if (to <= from) {
for (int i = 1; i <= zero; i++)
if (pos[0][i] > from) ans += 2;
for (int i = 1; i <= one; i++)
if (pos[1][i] > from) ans += 2;
for (int i = 1; i <= two; i++)
if (pos[2][i] > from) ans += 2;
} else {
if (type == 0) zero++;
else if (type == 1) one++;
else two++;
for (int i = zero + 1; i <= cnt[0]; i++)
if (pos[0][i] < from) ans += 2;
for (int i = one + 1; i <= cnt[1]; i++)
if (pos[1][i] < from) ans += 2;
for (int i = two + 1; i <= cnt[2]; i++)
if (pos[2][i] < from) ans += 2;
}
return ans;
}
int main() {
read(n);
scanf("\n%s", s + 1);
for (int i = 1; i <= n; i++) {
if (s[i] == 'V') a[i] = 0, pos[0][++cnt[0]] = i;
else if (s[i] == 'K') a[i] = 1, pos[1][++cnt[1]] = i;
else a[i] = 2, pos[2][++cnt[2]] = i;
}
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
for (int k = 0; k <= n; k++)
dp[i][j][k][0] = dp[i][j][k][1] = INF;
dp[0][0][0][0] = 0;
for (int i = 0; i <= n - 1; i++)
for (int j = 0; j <= cnt[0] && j <= i; j++)
for (int k = 0; k <= cnt[1] && j + k <= i; k++) {
int l = i - j - k, tmp;
tmp = dp[i][j][k][0];
if (j != cnt[0]) chkmin(dp[i + 1][j + 1][k][1], tmp + cost(0, j, k, l, i + 1, pos[0][j + 1]));
if (k != cnt[1]) chkmin(dp[i + 1][j][k + 1][0], tmp + cost(1, j, k, l, i + 1, pos[1][k + 1]));
if (l != cnt[2]) chkmin(dp[i + 1][j][k][0], tmp + cost(2, j, k, l, i + 1, pos[2][l + 1]));
tmp = dp[i][j][k][1];
if (j != cnt[0]) chkmin(dp[i + 1][j + 1][k][1], tmp + cost(0, j, k, l, i + 1, pos[0][j + 1]));
if (l != cnt[2]) chkmin(dp[i + 1][j][k][0], tmp + cost(2, j, k, l, i + 1, pos[2][l + 1]));
}
writeln(min(dp[n][cnt[0]][cnt[1]][0], dp[n][cnt[0]][cnt[1]][1]) / 2);
return 0;
}
**【D】**Bear and Rectangle Strips
【思路要点】
一个矩形可能占据一行,也可能占据两行。
我们若知道一个矩形的上、下、左边界,一定会选取右边界最靠左的一个合法的矩形。借助前缀和求出每个上、下、左边界对应的最靠左的右边界。
考虑一个 的 ,记 表示在保留第一行的前 列和第二行的前 列的情况下,最多能够选取的矩形数,显然有 转移。
如何优化该 呢?
我们发现对于任意一个合法的选取方案,我们可以规定按照被选取的矩形的右边界从左到右的顺序来选取矩形,这样做带来的好处是,在状态中我们去除了一些 和 相差巨大的冗余状态。
我们设新的 状态 表示原本的 , 表示最小的 使得 , 表示最小的 使得 。
这些信息已经足够我们转移了。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
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][2], r[MAXN][3], dp[MAXN][3];
long long s[MAXN][3];
map <long long, int> mp[3];
void update(int pos, int val, int zro, int one) {
if (val > dp[pos][2]) {
dp[pos][0] = zro;
dp[pos][1] = one;
dp[pos][2] = val;
} if (val == dp[pos][2]) {
chkmin(dp[pos][0], zro);
chkmin(dp[pos][1], one);
}
}
int main() {
read(n);
for (int t = 0; t <= 1; t++)
for (int i = 1; i <= n; i++)
read(a[i][t]);
for (int i = n; i >= 1; i--) {
s[i][0] = s[i + 1][0] + a[i][0];
s[i][1] = s[i + 1][1] + a[i][1];
s[i][2] = s[i + 1][2] + a[i][0] + a[i][1];
for (int j = 0; j <= 2; j++) {
r[i][j] = mp[j][s[i + 1][j]];
if (r[i][j] == 0) r[i][j] = r[i + 1][j];
mp[j][s[i + 1][j]] = i;
}
}
for (int i = 0; i <= 2; i++) {
r[0][i] = mp[i][s[1][i]];
if (r[0][i] == 0) r[0][i] = r[1][i];
}
for (int i = n; i >= 0; i--)
for (int j = 0; j <= 2; j++) {
if (r[i][j] == 0) r[i][j] = n + 1;
if (i != n) chkmin(r[i][j], r[i + 1][j]);
}
for (int i = 0; i <= n; i++)
dp[i][0] = r[i][0], dp[i][1] = r[i][1];
for (int i = 0; i <= n - 1; i++) {
int tmp = dp[i][2];
update(i + 1, tmp, dp[i][0], dp[i][1]);
update(dp[i][0], tmp + 1, r[dp[i][0]][0], r[i][1]);
update(dp[i][1], tmp + 1, r[i][0], r[dp[i][1]][1]);
update(r[i][0], tmp + 1, r[r[i][0]][0], dp[i][1]);
update(r[i][1], tmp + 1, dp[i][0], r[r[i][1]][1]);
if (r[i][0] == dp[i][1]) update(r[i][0], tmp + 2, r[r[i][0]][0], r[r[i][0]][1]);
if (dp[i][0] == r[i][1]) update(r[i][1], tmp + 2, r[r[i][1]][0], r[r[i][1]][1]);
update(r[i][2], tmp + 1, r[r[i][2]][0], r[r[i][2]][1]);
}
writeln(dp[n][2]);
return 0;
}
**【E】**Bear and Isomorphic Points
【思路要点】
首先,若 号点与其他两个点共线,那么显然新点也必须在这条线上,因此可行的面积为 ,下文我们考虑 号点不与其他任意两个点共线的情况。
一个 的做法是枚举一对点,新点必须与 号点在这一对点连成的直线的同侧,那么我们就可以通过半平面交来解决这个问题。
实际上,在这 个半平面中,有很多是冗余的,我们先给出本题的算法:
将所有其余点对 号点极角排序,令排序后的结果为 。
考虑半平面 。
令与点 极角相差不超过 ,且极角相差最大的点为 ,考虑半平面 。
运行半平面交即可。
为什么考虑上述半平面就足够了呢?
假设存在半平面 应该被考虑而上述算法没有考虑到,找到使得 最小的一对没有考虑到的 。
若存在 满足 与 在 的同侧,那么我们将考虑 和 ,从而不需要考虑 。
因此,所有 均满足 与 不在 的同侧,那么我们在第 步漏掉需要考虑的就是线段 ,而在第 步,我们一定会考虑到线段 或是一个更紧的限制,从而不需要考虑 。
综上,考虑上述半平面就足够了。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 400005;
const double eps = 1e-12;
const double bound = 1e6;
const double pi = acos(-1);
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 point {double x, y; };
struct info {point pos; double alpha; };
struct line {point a, b; double alpha; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, double b) {return (point) {a.x * b, a.y * b}; }
double operator * (point a, point b) {return a.x * b.y - a.y * b.x; }
double operator / (point a, point b) {
if (abs(b.x) > eps) return a.x / b.x;
else return a.y / b.y;
}
double moo(point a) {return sqrt(a.x * a.x + a.y * a.y); }
point unit(point a) {return a * (1.0 / moo(a)); }
double dist(point a, point b) {return moo(b - a); }
bool equal(double x, double y) {return fabs(x - y) <= eps; }
bool parallel(line x, line y) {return equal((x.b - x.a) * (y.b - y.a), 0); }
double PolarAngle(point a) {return atan2(a.y, a.x); }
int n, m, tot;
point p[MAXN];
line a[MAXN], q[MAXN];
point intersect(const line &x, const line &y) {
double tmp = (y.a - x.a) * (y.b - x.a);
double tnp = (y.b - x.b) * (y.a - x.b);
return (x.a * tnp + x.b * tmp) * (1 / (tmp + tnp));
}
bool onright(const line &l, const point &p) {
if ((p - l.a) * (l.b - l.a) > -eps) return true;
else return false;
}
bool onrightcmp(const line &l, const point &p) {
if ((p - l.a) * (l.b - l.a) > eps) return true;
else return false;
}
bool cmp(const line &a, const line &b) {
if (fabs(a.alpha - b.alpha) > eps) return a.alpha < b.alpha;
else return onrightcmp(a, b.a);
}
void HalfPlane(int n) {
sort(a + 1, a + n + 1, cmp);
int l = 0, r = -1;
for (int i = 1; i <= n; i++) {
while (l < r && onright(a[i], p[r])) r--;
while (l < r && onright(a[i], p[l + 1])) l++;
if (fabs(a[i].alpha - q[r].alpha) <= eps) continue;
if (a[i].alpha - q[r].alpha >= pi - eps) {
printf("%.10lf\n", 0.0);
return;
}
q[++r] = a[i];
if (l < r) p[r] = intersect(q[r], q[r - 1]);
}
while (l < r && onright(q[l], p[r])) r--;
while (l < r && onright(q[r], p[l + 1])) l++;
if (r - l <= 1) printf("%.10lf\n", 0.0);
else {
p[l] = intersect(q[l], q[r]);
double ans = 0;
for (int i = l; i <= r; i++) {
int j = i + 1;
if (i == r) j = l;
ans += p[i] * p[j];
}
printf("%.10lf\n", abs(ans) / 2);
}
}
info b[MAXN];
bool cnp(info a, info b) {
return a.alpha < b.alpha;
}
int main() {
read(tot);
while (tot--) {
read(n), m = 0;
point ld = (point) {-bound, -bound};
point rd = (point) {bound, -bound};
point ru = (point) {bound, bound};
point lu = (point) {-bound, bound};
a[++m] = (line) {ld, rd, PolarAngle(rd - ld)};
a[++m] = (line) {rd, ru, PolarAngle(ru - rd)};
a[++m] = (line) {ru, lu, PolarAngle(lu - ru)};
a[++m] = (line) {lu, ld, PolarAngle(ld - lu)};
for (int i = 1; i <= n; i++){
read(b[i].pos.x), read(b[i].pos.y);
b[i].alpha = PolarAngle(b[i].pos - b[1].pos);
}
sort(b + 2, b + n + 1, cnp);
for (int i = 2; i <= n; i++) {
b[i + n - 1] = b[i];
b[i + n - 1].alpha += 2 * pi;
}
bool flg = false;
for (int i = 2, j = 2; i <= n; i++) {
chkmax(j, i);
while (b[j + 1].alpha - b[i].alpha < pi + eps) j++;
if (equal(b[i + 1].alpha - b[i].alpha, 0) || equal(b[j].alpha - b[i].alpha, pi)) {
printf("%.10lf\n", 0.0);
flg = true;
break;
}
if (j != i) a[++m] = (line) {b[i].pos, b[j].pos, PolarAngle(b[j].pos - b[i].pos)};
if (b[i + 1].alpha - b[i].alpha < pi - eps) a[++m] = (line) {b[i].pos, b[i + 1].pos, PolarAngle(b[i + 1].pos - b[i].pos)};
}
if (flg) continue;
HalfPlane(m);
}
return 0;
}