2021年度训练联盟热身训练赛第一场
https://ac.nowcoder.com/acm/contest/12606#question
| A | Weird Flecks, But OK
给你n个三维空间内的点,你有一个钻头,可以从平行于坐标轴(x,y,z)方向钻入,问最小直径能覆盖所有点
显然平行x轴即为去掉了x轴坐标,留下二维平面坐标系,即求3次二维平面内的最小覆盖圆,取最小值
struct vec {
double x, y;
vec(const double& x0 = 0, const double& y0 = 0) : x(x0), y(y0) {};
vec operator + (const vec& t) const { return vec(x + t.x, y + t.y); }
vec operator - (const vec& t) const { return vec(x - t.x, y - t.y); }
vec operator * (const double& t) const { return vec(x * t, y * t); }
vec operator / (const double& t) const { return vec(x / t, y / t); }
const double len2() const { return x * x + y * y; }
const double len() const { return sqrt(len2()); }
vec norm() const { return *this / len(); }
vec rotate_90_c() { return vec(y, -x); }
};
double dot(const vec& a, const vec& b) { return a.x * b.x + a.y * b.y; }//点积
double crs(const vec& a, const vec& b) { return a.x * b.y - a.y * b.x; }//叉积
vec lin_lin_int(const vec& p0, const vec& v0, const vec& p1, const vec& v1){//三点两线求中垂线交点
double t = crs(p1 - p0, v1) / crs(v0, v1);
return p0 + v0 * t;
}
vec circle(const vec& a, const vec& b, const vec& c){//三点求圆心
return lin_lin_int((a + b) / 2, (b - a).rotate_90_c(), (a + c) / 2, (c - a).rotate_90_c());
}
int n;
vec pot1[100005], pot2[100005], pot3[100005];
double min_cover_circle(int n, vec *pot) {
random_shuffle(pot + 1, pot + n + 1);// 将输入的数据随机打乱,防恶心数据
vec o;
double r2 = 0;
for (int i = 1; i <= n; i++) {
if ((pot[i] - o).len2() > r2) {
o = pot[i], r2 = 0;
for (int j = 1; j < i; j++) {
if ((pot[j] - o).len2() > r2) {
o = (pot[i] + pot[j]) / 2, r2 = (pot[j] - o).len2();
for (int k = 1; k < j; k++) {
if ((pot[k] - o).len2() > r2) {
o = circle(pot[i], pot[j], pot[k]), r2 = (pot[k] - o).len2();
}
}
}
}
}
}
//printf("%.10lf\n%.10lf %.10lf\n", sqrt(r2), o.x, o.y);
return sqrt(r2);
}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
double x, y, z;
scanf("%lf%lf%lf", &x, &y, &z);
pot1[i] = { x, y };
pot2[i] = { x, z };
pot3[i] = { y, z };
}
printf("%.10lf\n", 2 * min(min_cover_circle(n, pot1), min(min_cover_circle(n, pot2), min_cover_circle(n, pot3))));
return 0;
}
| B | Code Names |
给你n个小写字母组成的单词,每个单词没有相同的字母,即最长26
规定一个单词交换两个字母位置后与另一个单词相同即有关系,不可以共存
问单词的最大集合
没有想到3点之间必不成环的关系
染色后建二分图跑二分图最大匹配
char s[550][30];
vector<int> v[550];
int head[550], vis[550], cnt, h[550], ans;
struct edge {
int u, c, nxt;
edge() {};
edge(int u, int c, int nxt) : u(u), c(c), nxt(nxt) {};
}e[550 * 550];
void add(int u, int v, int c) {
e[cnt] = { v,c,head[u] }; head[u] = cnt++;
e[cnt] = { u,0,head[v] }; head[v] = cnt++;
}
const int inf = 0x3f3f3f3f;
int sta, t;
bool bfs() {
for (int i = 0; i < 550; ++i) h[i] = head[i];
memset(vis, -1, sizeof(vis));
queue<int> q;
q.push(sta);
vis[sta] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = h[u]; ~i; i = e[i].nxt) {
int w = e[i].u;
if (vis[w] == -1 && e[i].c > 0) {
vis[w] = vis[u] + 1;
q.push(w);
}
}
}
return vis[t] != -1;
}
ll findf(int x, int flow) {
if (x == t) return (ll)flow;
ll res = 0;
for (int& i = h[x]; ~i; i = e[i].nxt) {
int w = e[i].u;
if (vis[w] == vis[x] + 1 && e[i].c > 0) {
int ff = findf(w, min(e[i].c, flow));
if (!ff)continue;
e[i].c -= ff; e[i ^ 1].c += ff;
res += ff;
flow -= ff;
if (!flow) break;
}
}
if (!res) vis[x] = -1;
return res;
}
int dinic() { int ans = 0; while (bfs())ans += findf(sta, inf); return ans; }
int col[550];
int vis2[550];
void dfs(int u) {
vis2[u] = 1;
for (auto v : v[u]) {
if (vis2[v]) continue;
col[v] = col[u] ^ 1;
dfs(v);
}
}
bool check(int a, int b) {
int len1 = strlen(s[a]);
int len2 = strlen(s[b]);
if (len1 != len2) return 0;
int pos = -1, cnt = 0;
for (int i = 0; i < len1; ++i) {
if (s[a][i] == s[b][i]) continue;
if (pos == -1) pos = i, cnt++;
else if (cnt == 1) {
cnt++;
if (s[a][i] != s[b][pos] || s[b][i] != s[a][pos]) return 0;
}
else return 0;
}
return 1;
}
signed main() {
int n; scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%s", s[i]);
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (check(i, j)) {
v[i].push_back(j);
v[j].push_back(i);
}
}
}
sta = n, t = n + 1;
memset(head, -1, sizeof head);
for (int i = 0; i < n; ++i) if (!vis[i]) dfs(i);
for (int i = 0; i < n; ++i) {
if (col[i]) add(i, t, 1);
else {
add(sta, i, 1);
for (auto v : v[i]) add(i, v, 1);
}
}
printf("%d\n", n - dinic());
}
| C | New Maths |
可以发现当前面的位知道以后该位最多有2种可能
所以可以预处理后跑dfs,复杂度符合
char s[50];
int num[50];
int fir[10][10];
int se[10][10][10];
void init() {
for (int i = 0; i < 10; ++i) {
int tmp = i * i % 10;
fir[tmp][++fir[tmp][0]] = i;
}
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
int tmp = i * j * 2 % 10;
se[i][tmp][++se[i][tmp][0]] = j;
}
}
}
int cnt = 0, len = 0, fp = -1, limit = 0;
int ans[50];
bool flag = 0;
inline int qmod(int x) {
if (x >= 10) x -= 10;
return x;
}
bool check(int pos) {
int tpos = pos;
for (int i = 1; i < limit; ++i, ++ tpos) {
int tmp = 0, up = tpos / 2;
for (int j = i; j <= up; ++j) {
if (j == tpos - j) tmp += ans[j] * ans[tpos - j];
else tmp += 2 * ans[j] * ans[tpos - j];
}
tmp %= 10;
//printf("%d\n", tmp);
if (tmp != num[len - 1 - tpos]) return 0;
}
return 1;
}
ll o[10000], ocnt = 0;
void dfs(int pos) {
//if (flag) return;
if (pos == limit) {
//printf("1\n");
if (check(pos)) {
ll tmp = 0;
for (int i = limit - 1; i >= 0; --i) tmp = tmp * 10 + ans[i];
o[ocnt++] = tmp;
}
return;
}
int tmp = 0, up = pos / 2;
for (int i = 1; i <= up; ++i) {
if (i == pos - i) tmp += ans[i] * ans[pos - i];
else tmp += 2 * ans[i] * ans[pos - i];
}
tmp %= 10;
int op = qmod(num[len - 1 - pos] - tmp + 10);
for (int i = 1; i <= se[fp][op][0]; ++i) {
//if (flag) break;
ans[pos] = se[fp][op][i]; //printf("ans[%d] = %d\n", pos, ans[pos]);
dfs(pos + 1);
}
}
signed main() {
init();
scanf("%s", s);
len = strlen(s);
if (!(len & 1)) {
puts("-1");
return 0;
}
limit = (len + 1) / 2;
for (int i = 0; i < len; ++i) num[i] = (int)s[i] - '0';
for (int j = 1; j <= fir[num[len - 1]][0]; ++j) {
//if (flag) break;
fp = fir[num[len - 1]][j]; ans[0] = fp;
//printf("fp = %d\n", fp);
dfs(1);
}
if (ocnt == 0) printf("-1");
else {
sort(o, o + ocnt); printf("%lld\n", o[0]);
}
puts("");
return 0;
}
| D | Some Sum |
签到题,略
| E | Early Orders |
一道思维题,实在没能想出来
给你n张牌,每张牌数字为1到k
输出字典序最小的包含1到k的长为k的子序列
从前往后遍历,如果当前答案数组中没有这个数,该数和当前答案数组最后一个数大小比较,如果更小而且前一个数在后面的序列中还有,删掉那个数,
一直比较直到大于前面那个数,放入,如果已经有这个数则跳过
int a[maxn], cnt[maxn], ans[maxn];
int ed, vis[maxn];
signed main() {
int n, k;
scanf("%d %d", &n, &k);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]); cnt[a[i]] ++;
}
for (int i = 0; i < n; ++i) {
cnt[a[i]] --;
if (vis[a[i]]) continue;
ans[ed++] = a[i];
for (int j = ed - 2; j >= 0; --j) {
if (a[i] < ans[j] && cnt[ans[j]]) {
ed--; vis[ans[j]] = 0; ans[j] = a[i];
}
else break;
}
vis[a[i]] = 1;
}
for (int i = 0; i < ed; ++i) printf("%d%c", ans[i], i == ed - 1 ? '\n' : ' ');
}