2022“杭电杯”中国大学生算法设计超级联赛(6)

Map

给两个矩形表示两幅比例不同的地图,求一个点在两幅地图上表示同一个地理位置。
设两幅地图大小比例k,即求一个点到A点和a点距离之比为k,到B点和b点距离之比为k的点。
直接求出两个圆,再求交点,取在小矩形内的点(求交点的代码copy的)

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-15;
struct point {
	double x, y;
	double &operator [](int u) { if (u == 0) return x; else return y; }
	point(double x = 0, double y = 0) : x(x), y(y) {}
	point(const point &rhs) : x(rhs.x), y(rhs.y) {}
	point operator +(const point &rhs) { return point(x + rhs.x, y + rhs.y); }
	point operator -(const point &rhs) { return point(x - rhs.x, y - rhs.y); }
	point operator /(const double &rhs) { return point(x / rhs, y / rhs); }
	point operator *(const double &rhs) { return point(x * rhs, y * rhs); }
	double norm() { return sqrt(x * x + y * y); }
} A, B, C, D, a, b, c, d;
double dis(point a, point b) { return (a - b).norm(); }
double dot(point a, point b) { return a.x * b.x + a.y * b.y; }
double cross(point a, point b) { return a.x * b.y - a.y * b.x; }
vector<point> intersectionofcircles(point C1, const double &R1, point C2, const double &R2)
{
	vector<point> i_Result; i_Result.clear();
	double d = dis(C1, C2);//圆心距d
	if (abs(d) < eps){}//重合,返回空
	else {
		double a = (d*d + R1*R1 - R2*R2) / (2 * d);
		if (abs(a*a-R1*R1)<eps) {
			point target;
			double t = R1 / d;
			target[0] = C1[0] * (1 - t) + C2[0] * t;
			target[1] = C1[1] * (1 - t) + C2[1] * t;
			i_Result.push_back(target);
		} else if (a*a<R1*R1) {
			double h = sqrt((R1*R1 - a*a));
			//C1->C2的单位方向向量计算
			point Dv((C2[0] - C1[0])/d, (C2[1] - C1[1])/d);
			//C1->C2的单位法向量计算
			point Nv;
			if (Dv[0]==0) {
				Nv[0] = 1;
				Nv[1] = 0;
			} else if (Dv[1] == 0) {
				Nv[0] = 0;
				Nv[1] = 1;
			} else {
				Nv[0] = -1 / Dv[0];
				Nv[1] = 1 / Dv[1];
				double unify = sqrt(Nv[0]*Nv[0]+Nv[1]*Nv[1]);
				Nv[0] = Nv[0] / unify;
				Nv[1] = Nv[1] / unify;
            }
			point p1, p2;
			p1[0] = C1[0] + a*Dv[0] + h*Nv[0];
			p1[1] = C1[1] + a*Dv[1] + h*Nv[1];
			p2[0] = C1[0] + a*Dv[0] - h*Nv[0];
			p2[1] = C1[1] + a*Dv[1] - h*Nv[1];
			i_Result.push_back(p1);
			i_Result.push_back(p2);
		}
	}
	return i_Result;
}
bool check(point x) {
	if (cross(d - a, x - a) > eps && cross(x - a, b - a) > eps) {
		printf("%.6lf %.6lf\n", x.x, x.y);
		return 1;
	}
	return 0;
}
int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &A.x, &A.y, &B.x, &B.y, &C.x, &C.y, &D.x, &D.y);
		scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &a.x, &a.y, &b.x, &b.y, &c.x, &c.y, &d.x, &d.y);
		double k = dis(a, b) / dis(A, B);
//		cerr << k << endl;
		point a1 = A + (a - A) / (k + 1), a2 = a + (a - A) * k / (1 - k);
		point o1 = (a1 + a2) / 2;
		double r1 = dis(a1, a2) / 2;
//		cerr << "#" << o1.x << " " << o1.y << " " << r1 << endl;
		point d1 = D + (d - D) / (k + 1), d2 = d + (d - D) * k / (1 - k);
		point o2 = (d1 + d2) / 2;
		double r2 = dis(d1, d2) / 2;
		for (auto &x : intersectionofcircles(o1, r1, o2, r2))
			if (check(x))
				break;
	}
	return 0;
}

Planar graph

给一副平面图,即可以画成这样的图
求一个字典序最小的边序列,使得删除对应边后,所有空白区域联通。
可以倒着考虑每条边,没联通就加入,联通的就输出对应边。

#include <bits/stdc++.h>
using namespace std;
int a[200005], fa[200005], b[200005];
bool flag[200005], vis[200005];
vector<int> G[200005];
int gf(int x) { return x == fa[x] ? x : fa[x] = gf(fa[x]); }
void dfs(int u) {
	vis[u] = 1;
	for (int v : G[u])
		if (!vis[v])
			dfs(v);
}
int main() {
	int T; scanf("%d", &T);
	while (T--) {
		int n, m; scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i) {
			G[i].clear();
			vis[i] = 0;
		}
		for (int i = 1; i <= m; ++i) {
			scanf("%d%d", &a[i], &b[i]);
			G[a[i]].push_back(b[i]);
			G[b[i]].push_back(a[i]);
			flag[i] = 0;
		}
		int left = n;
		for (int i = 1; i <= n; ++i)
			if (!vis[i]) {
				dfs(i);
				--left;
			}
		for (int i = 1; i <= n; ++i) fa[i] = i;
		int j = m;
		for (int i = 1; i <= left; ++i) {
			int x = gf(a[j]), y = gf(b[j]);
			while (x == y) {
				--j;
				x = gf(a[j]), y = gf(b[j]);
			}
			fa[x] = y;
			flag[j] = 1;
		}
		printf("%d\n", m - left);
		for (int i = 1; i <= m; ++i)
			if (!flag[i]) printf("%d ", i);
		puts("");
	}
	return 0;
}

Loop

给一个数组a,每次可以选一个区间[l,r]可以把该区间的第一个元素放到该区间的最后,原数组其他位置不受影响。问k次操作后最大的字典序多少
找n-k长的子序列放在开头,其余的从大到小排序放在末尾。只需找个最大的子序列即可。

#include <bits/stdc++.h>
using namespace std;
int n, m, a[300005], q[300005];
bool use[300005];
int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		m = min(m, n);
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			use[i] = 0;
		}
		int j = 0, h = 1, t = 0;
		vector<int> pt, ans;
		for (int i = n - m; i; --i) {
			while (j < n - i + 1) {
				int x = a[++j];
				while (h <= t && x > a[q[t]]) --t;
				q[++t] = j;
			}
			use[q[h]] = 1;
			pt.emplace_back(a[q[h++]]);
		}
		for (int i = 1; i <= n; ++i)
			if (!use[i]) ans.emplace_back(a[i]);
		sort(ans.begin(), ans.end(), greater<int>());
		for (int x : ans) pt.emplace_back(x);
		for (int i = 0; i < n - 1; ++i) printf("%d ", pt[i]);
		printf("%d\n", pt[n - 1]);
	}
	return 0;
}
posted @ 2022-08-06 00:01  chenyilei  阅读(34)  评论(0编辑  收藏  举报