Day1T1 建筑装饰4
题目链接:Day1T1 建筑装饰4
Solution
我们先考虑朴素的(dp)方法:
设(dp_{i,j,k})表示前(i)个数中,选了(j)个(B)数组中的数,并且第(i)个数选的是(A/B)时,是否存在满足单调不降的数列。
复杂度:(O(n^2)),期望得分:(11)分。
考虑如何优化。可以打个表,发现当(i,k)固定的时候,满足(dp_{i,j,k}=1)的(j)刚好是一个区间。
我们可以大胆猜结论:如果(dp_{i,j_1,k}=1)并且(dp_{i,j_2,k}=1),那么对于任意(j_1le jle j_2),都满足(dp_{i,j,k}=1)。
于是,我们可以记录数组(l_{i,k})和(r_{i,k})分别表示最小的(j)和最大的(j)。
输出路径的时候从后往前找即可。
复杂度:(O(n)),期望得分:(100)分。
Code
// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int inf = 1e9;
const int N = 1000005;
int l[N][2], r[N][2];
int a[N][2], n;
void ckmin(int &a, int b) {
if (a > b) a = b;
}
void ckmax(int &a, int b) {
if (a < b) a = b;
}
int main() {
// freopen("building.in", "r", stdin);
// freopen("building.out", "w", stdout);
n = read();
a[0][0] = a[0][1] = -inf, a[2 * n + 1][0] = a[2 * n + 1][1] = inf;
for (rint i = 1; i <= 2 * n; i++) a[i][0] = read();
for (rint i = 1; i <= 2 * n; i++) a[i][1] = read();
for (rint i = 1; i <= 2 * n; i++) {
for (rint j = 0; j <= 1; j++) {
l[i][j] = inf, r[i][j] = -inf;
for (rint k = 0; k <= 1; k++) {
if (a[i][j] >= a[i - 1][k]) {
ckmin(l[i][j], l[i - 1][k] + (j == 0));
ckmax(r[i][j], r[i - 1][k] + (j == 0));
}
}
}
}
stack <int> ans;
if (l[2 * n][0] <= n && n <= r[2 * n][0]) {
int lef = n, st = 0;
ans.push(0);
for (rint i = 2 * n - 1; i >= 1; i--) {
if (st == 0) {
lef--;
for (rint j = 0; j <= 1; j++) {
if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
st = j;
break;
}
}
} else {
for (rint j = 0; j <= 1; j++) {
if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
st = j;
break;
}
}
}
ans.push(st);
}
while (!ans.empty()) {
if (ans.top() == 0) putchar('A');
else putchar('B');
ans.pop();
}
puts("");
} else if (l[2 * n][1] <= n && n <= r[2 * n][1]) {
int lef = n, st = 1;
ans.push(1);
for (rint i = 2 * n - 1; i >= 1; i--) {
if (st == 0) {
lef--;
for (rint j = 0; j <= 1; j++) {
if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
st = j;
break;
}
}
} else {
for (rint j = 0; j <= 1; j++) {
if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
st = j;
break;
}
}
}
ans.push(st);
}
while (!ans.empty()) {
if (ans.top() == 0) putchar('A');
else putchar('B');
ans.pop();
}
puts("");
} else {
puts("-1");
}
return 0;
}
Day1T2 汉堡肉
题目链接:Day1T2 汉堡肉
Solution
对于(k=1),显然这个点位于这(n)个矩形的交。
复杂度:(O(n)),期望得分:(2)分。
对于(k=2),令(r=min_{i=1}^{n}R_i),令其中一个点的横坐标为(r),同时枚举它所有的可能纵坐标,并且包含这个点的矩形删掉。
剩余的矩形按(k=1)的做法即可得到另一个点。
复杂度:(O(n^2)),期望得分:(1)分,结合(k=1)可得(3)分。
考虑如何优化。我们先将所有的矩形按照(R_i)优化,然后在扫描线的过程中记录剩余矩形中的(max(L_i),max(D_i),min(R_i),min(U_i))。
具体过程可以用优先队列来实现。
复杂度:(O(nlogn)),期望得分:(3)分,结合(k=1)可得(6)分。
对于剩下的点,我采用了随机化做法。
先随机打乱这(n)个矩形,然后将前(k)个矩形分别放置这(k)类。
接下来,将第(k+1到n)这些矩形依次插入这(k)类。
定义估价函数(h(rec_x,rec_y)=frac{area(merge(rec_x,rec_y))}{area(rec_y)})表示覆盖率。
我们可以去寻找第(i)个矩形与第(j)类矩阵交后的估价函数最大值。
那么估价函数越高,即越优秀,匹配成功的概率最大。
按照如上方式随机化,可以通过此题。
复杂度:(O(欧皇)),期望得分:(100)分。
Code
// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 200005;
int n, k;
struct rect {
ll x1, y1, x2, y2;
rect(ll _x1 = 0, ll _y1 = 0, ll _x2 = 0, ll _y2 = 0) {
x1 = _x1, y1 = _y1, x2 = _x2, y2 = _y2;
}
} a[N], b[5];
rect merge(rect x, rect y) {
return rect(max(x.x1, y.x1), max(x.y1, y.y1), min(x.x2, y.x2), min(x.y2, y.y2));
}
ll area(rect x) { // 矩形内包含多少点
if (x.x1 > x.x2 || x.y1 > x.y2) return 0;
else return (x.x2 - x.x1 + 1) * (x.y2 - x.y1 + 1);
}
double h(rect x, rect y) {
rect m = merge(x, y);
return area(m) / 1.0 / area(y);
}
int main() {
srand(time(NULL));
scanf("%d%d", &n, &k);
for (rint i = 1; i <= n; i++) {
scanf("%lld%lld%lld%lld", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
}
while (true) {
int ok = 1;
random_shuffle(a + 1, a + n + 1);
for (rint i = 1; i <= k; i++) b[i] = a[i];
for (rint i = k + 1; i <= n; i++) {
double max_h = -1e9; int id = 1;
for (rint j = 1; j <= k; j++) {
double _h = h(a[i], b[j]);
if (_h > max_h) {
max_h = _h;
id = j;
}
}
b[id] = merge(b[id], a[i]);
if (area(b[id]) <= 0) {
ok = 0;
break;
}
}
if (ok) break;
}
for (rint i = 1; i <= k; i++) {
printf("%lld %lld
", b[i].x1, b[i].y1);
}
return 0;
}
谈谈正经的做法。
// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define all(x) x.begin(), x.end()
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
inline void ckmin(int &a, int b) {
if (a > b) a = b;
}
inline void ckmax(int &a, int b) {
if (a < b) a = b;
}
const int N = 1500005;
const int inf = 1e9;
struct rect {
int x1, y1, x2, y2;
} a[N];
int n, K;
pair <int, int> ans[5];
int cnt[N];
void modify(int x, int y, int v) {
for (rint i = 1; i <= n; i++) {
if (a[i].x1 <= x && x <= a[i].x2 && a[i].y1 <= y && y <= a[i].y2) {
cnt[i] += v;
}
}
}
void dfs(int d) {
int x1 = -inf, x2 = inf, y1 = -inf, y2 = inf;
for (rint i = 1; i <= n; i++) if (!cnt[i]) {
ckmax(x1, a[i].x1), ckmax(y1, a[i].y1);
ckmin(x2, a[i].x2), ckmin(y2, a[i].y2);
}
if (d == 1) {
if (x1 <= x2 && y1 <= y2) { // 最后一个点在剩余矩形的交内
ans[1] = make_pair(x1, y1);
for (rint i = 1; i <= K; i++) {
printf("%d %d
", ans[i].first, ans[i].second);
}
exit(0);
}
return ;
}
for (rint cx = 0; cx < 2; cx++) {
for (rint cy = 0; cy < 2; cy++) {
int px = (cx ? x1 : x2);
int py = (cy ? y1 : y2);
modify(px, py, 1);
ans[d] = make_pair(px, py);
dfs(d - 1);
modify(px, py, -1);
}
}
}
vector <int> h[N];
vector <tuple <int, int, int>> sl[4], sr[4];
int pre[4][N], suf[4][N], id[N][4];
int fir(tuple <int, int, int> a) {
return get<0>(a);
}
int sec(tuple <int, int, int> b) {
return get<1>(b);
}
int thi(tuple <int, int, int> c) {
return get<2>(c);
}
vector <int> G[N];
void add(int u, int v) { G[u].push_back(v); }
int dfn[N], low[N], dtot;
int ins[N], be[N], scc;
stack <int> st;
void tarjan(int u) {
dfn[u] = low[u] = ++dtot;
st.push(u), ins[u] = 1;
for (auto v: G[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (ins[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
int y; ++scc;
while (y = st.top()) {
ins[y] = 0, st.pop();
be[y] = scc;
if (y == u) break;
}
}
}
void solve() {
int x1 = -inf, x2 = inf, y1 = -inf, y2 = inf;
for (rint i = 1; i <= n; i++) {
ckmax(x1, a[i].x1), ckmax(y1, a[i].y1);
ckmin(x2, a[i].x2), ckmin(y2, a[i].y2);
}
//cerr << "ok1 !
";
//assert(x1 >= x2 && y1 >= y2);
int tot = 0, min_h = inf;
for (rint i = 1; i <= n; i++) {
vector <int> o = h[i];
// 两条纵边 (0 <= id <= 1)
if (a[i].x1 <= x1 && x1 <= a[i].x2) o.pb(0);
if (a[i].x1 <= x2 && x2 <= a[i].x2) o.pb(1);
// 两条横边 (2 <= id <= 3)
if (a[i].y1 <= y1 && y1 <= a[i].y2) o.pb(2);
if (a[i].y1 <= y2 && y2 <= a[i].y2) o.pb(3);
ckmin(min_h, o.size());
if (o.size() >= 3) { // 一定与一条边有交
//o.clear();
continue;
}
id[i][0] = ++tot, id[i][1] = ++tot;
if (o.size() == 1) add(id[i][1], id[i][0]);
int siz = o.size();
for (rint j = 0; j < siz; j++) {
sl[o[j]].emplace_back(o[j] < 2 ? max(y2, a[i].y1) : max(x2, a[i].x1), id[i][j], id[i][j ^ 1]);
sr[o[j]].emplace_back(o[j] < 2 ? min(y1, a[i].y2) : min(x1, a[i].x2), id[i][j], id[i][j ^ 1]);
}
//o.pb(0);
h[i] = o;
//printf("%d
", h[i].size());
}
//exit(0);
//cerr << "min_h" << ' ' << min_h << '
';
//assert(min_h > 0);
//cerr << "ok2 !
";
for (rint v = 0; v < 4; v++) {
sort(all(sl[v])), sort(all(sr[v]));
int sz_l = sl[v].size(), sz_r = sr[v].size();
for (rint i = 0; i < sz_r; i++) {
pre[v][i + 1] = ++tot;
if (i > 0) add(pre[v][i + 1], pre[v][i]);
add(pre[v][i + 1], thi(sr[v][i]));
}
for (rint i = 0, j = 0; i < sz_l; i++) {
while (j < sz_r && fir(sr[v][j]) < fir(sl[v][i])) j++;
if (j > 0) add(sec(sl[v][i]), pre[v][j]);
}
for (rint i = sz_l - 1; i >= 0; i--) {
suf[v][i] = ++tot;
if (i + 1 < sz_l) add(suf[v][i], suf[v][i + 1]);
add(suf[v][i], thi(sl[v][i]));
}
for (rint i = sz_r - 1, j = sz_l; i >= 0; i--) {
while (j > 0 && fir(sl[v][j - 1]) > fir(sr[v][i])) j--;
if (j < sz_l) add(sec(sr[v][i]), suf[v][j]);
}
}
//printf("tot=%d
", tot);
//for (rint i = 1; i <= tot; i++) {
// printf("%d
", G[i].size());
//}
//exit(0);
//cerr << "ok3 !
";
for (rint i = 1; i <= tot; i++) if (!dfn[i]) {
tarjan(i);
}
//cerr << "ok4 !
";
vector <int> L(5), R(4);
L[0] = L[1] = y2, L[2] = L[3] = x2;
R[0] = R[1] = y1, R[2] = R[3] = x1;
//cerr << "ok5 !
";
for (rint i = 1; i <= n; i++) {
if (!id[i][0]) continue;
assert(h[i].size());
int j = h[i][be[id[i][0]] >= be[id[i][1]]]; // 拓扑序反过来
//printf("%d: %d
", i, be[id[i][0]] >= be[id[i][1]]);
ckmax(L[j], j < 2 ? a[i].y1 : a[i].x1);
ckmin(R[j], j < 2 ? a[i].y2 : a[i].x2);
}
//cerr << "ok6!
";
assert(L[0] <= R[0] && L[1] <= R[1] && L[2] <= R[2] && L[3] <= R[3]);
printf("%d %d
", x2, L[1]);
printf("%d %d
", x1, L[0]);
printf("%d %d
", L[3], y2);
printf("%d %d
", L[2], y1);
}
int main() {
//freopen("04-14.txt.in", "r", stdin);
//freopen("my.out", "w", stdout);
scanf("%d%d", &n, &K);
for (rint i = 1; i <= n; i++) {
scanf("%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
}
dfs(K);
assert(K == 4);
solve();
return 0;
}
Day1T3 扫除
题目链接:Day1T3 扫除
Solution
Code
咕咕咕
Day2T1 变色龙之恋
题目链接:Day2T1 变色龙之恋
Day2T2 有趣的Joitter交友
题目链接:Day2T2 有趣的Joitter交友
Day2T3 遗迹
题目链接:Day2T3 遗迹
Day3T1 星座3
题目链接:Day3T1 星座3
Day3T2 收获
题目链接:Day3T2 收获
Day3T3 迷路的猫
题目链接:Day3T3 迷路的猫
Day4T1 首都
题目链接:Day4T1 首都
Day4T2 制作团子
题目链接:Day4T2 制作团子
Day4T3 应对方案
题目链接:Day4T3 应对方案