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' : ' ');
}

posted @ 2021-03-08 22:01  wansheking  阅读(21)  评论(0)    收藏  举报