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;
}