闲话 23.3.28

发布时间 2023-03-28 15:21:42作者: joke3579

闲话

今天中午放的是 cd 写的 映山红
本来想在博客里开骂的
image
想了想算了 没啥必要
还是说人和人的品味是互相独立的吧
但是常听隔着年代的歌
这情况大概只能是家长的灌输了吧
我不好评价了

模拟赛

翻了 但是完全没翻!

T1
贪心构造。
我们先把每个点和他对应区间的最小值和最大值连边,过程中并查集维护连通性。然后对每个点扫区间,有能加的边就加上。可以反证法证明正确性?
总时间复杂度 \(O(n^2)\)

code
#include <bits/stdc++.h>
using namespace std;
using ll = long long; using pii = pair<int, int>; using vi = vector<int>; using vp = vector<pii>;
#define rep(i,s,t) for (register int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (register int i = (s), i##END = (t) - 1; i > i##END; -- i)
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(fl) freopen(#fl".in", "r", stdin), freopen(#fl".out", "w", stdout)
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n'
#define eb emplace_back
const int N = 1e6 + 10;
int n; pii a[N]; vp ans;

int dsu[N];
int find(int u) { return u == dsu[u] ? u : dsu[u] = find(dsu[u]); }
void adde(int u, int v) {
    if (find(u) == find(v)) return;
    if (u > v) swap(u, v);
    ans.eb(u, v); dsu[find(u)] = find(v);
}

pii b[N];
inline bool check() {
    rep(i,1,n) b[i].first = 1e9 + 7, b[i].second = -1;
    for (auto [u, v] : ans) {
        b[u].first = min(b[u].first, v);
        b[u].second = max(b[u].second, v);
        b[v].first = min(b[v].first, u);
        b[v].second = max(b[v].second, u);
    } 
    rep(i,1,n) if (b[i].first != a[i].first or b[i].second != a[i].second) return 0;
    iota(dsu, dsu + n + 1, 0);
    for (auto [u, v] : ans) {
        dsu[find(u)] = find(v);
    } 
    rep(i,1,n) if (find(i) != find(1)) return 0;
    return ans.size() == n - 1;
}

signed main() {
    cin >> n; iota(dsu, dsu + n + 1, 0);
    rep(i,1,n) cin >> a[i].first >> a[i].second;
    rep(i,1,n) adde(i, a[i].first), adde(i, a[i].second);
    rep(i,1,n) {
        if (a[i].first < a[i].second) {
            rep(j, a[i].first, a[i].second) {
                if (a[j].first <= i and i <= a[j].second) 
                    adde(i, j);
            }
        }
    } 
    if (!check()) cout << "-1" << '\n';
    else for (auto [u, v] : ans) cout << u << ' ' << v << '\n';
}

T2
如果没有 key obs. 的话最多是能拿 92pts 吗?不知道有没有没看出来就切了的人啊!
没观察出来的话可以发现可以先把叶子找出来,并维护目前找到的子树区间以及根,然后直接向两边拓展区间、拼合区间。加一点随机化就可以 \(200\) 次操作过第三个包了。
key obs. 是 \(1\) 号节点肯定没有左儿子,所以从 \(1\) 号节点开始,每次向右拓展一个节点。
如果本节点没有右儿子,则这棵子树的范围和根已经确定了,直接连在 \(i + 1\) 上即可。
反之右子树的左端点确定,递归求解即可。
询问次数是 \(2n\) 的。

code
#include <bits/stdc++.h>
#include "interact.h"
using namespace std;
#define rep(i, s, t) for (register int i = (s), i##END = (t) + 1; i < i##END; ++i)
const int N = 100 + 10;
int lim, pre[N], mnv[N], mxv[N];

bool query_(int u, int l, int r) {
    if (!u) {
        if (l == 0 and r == lim) return true;
        return false;
    } return query(u, l, r);
}

void report_(int u, int v) {
    if (u) {
        report(u, v);
        mnv[u] = min(mnv[u], mnv[v]);
        mxv[u] = max(mxv[u], mxv[v]);
    }
}

void solve(int u) {
    if (!query_(u, mnv[u], mxv[u])) {
        pre[u + 1] = u;
        solve(u + 1);
    } else {
        int p = u;
        while (p) {
            if (!query_(pre[p], mnv[pre[p]], mxv[p])) {
                report_(u + 1, p);
                pre[u + 1] = pre[p];
                solve(u + 1);
                break;
            } else {
                report_(pre[p], p);
                p = pre[p];
            }
        }
    }
}

void guess(int n) {
    lim = n;
    mnv[0] = 0, mxv[0] = n;
    rep(i,1,n) pre[i] = 0, mnv[i] = mxv[i] = i;
    pre[1] = 0;
    solve(1);
}

T3
咋搞的都有。场上的思路是按 \(2k + 1\) 分组,每 \(2k + 1\) 个定能组成一个最优子结构,剩下的节点按大小分,看是自成一组还是加入前面的一堆节点。但是好像比较难写。
所以写了个乱搞作法。首先承认这样一个结论:最终的节点度数一定只有 \(k\)\(k + 1\)。这样我们就可以枚举度数为 \(k\) 的节点数量 \(x\),取 \(x\) 的最大值构造。
构造的话首先将前 \(x\) 个节点和后 \(x + 1\) 个节点分离,前 \(x\) 个节点和后 \(x + 1\) 个节点间需要连 \(kx\) 条边,每次取后 \(x + 1\) 个节点中度数最小的即可。
\(x + 1\) 个节点自己再连,拿个 set 维护一下重边即可。
取最小可以用堆,总时间复杂度不会证。

code
#include <bits/stdc++.h>
using namespace std;
using ll = long long; using pii = pair<int, int>; using vi = vector<int>; using vp = vector<pii>;
#define rep(i,s,t) for (register int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (register int i = (s), i##END = (t) - 1; i > i##END; -- i)
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(fl) freopen(#fl".in", "r", stdin), freopen(#fl".out", "w", stdout)
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n'
#define eb emplace_back
const int N = 1e6 + 10;
int n, k, cnt;

struct el {
	el(int id = 0, int val = 0, bool ck = 0) 
		: id(id), val(val), ck(ck) {}
	int id, val; bool ck;
	bool operator < (const el& b) const {
		if (val != b.val) return val > b.val;
		if (ck and !b.ck) return false;
		return id > b.id;
	}
}; 
priority_queue<el> que;
vector<el> vec;

struct hash_ {
	template <typename T>
	size_t operator() (T p) const {
		if (p.first > p.second) swap(p.first, p.second);
		return (size_t) p.first * 13331 + (size_t) p.second;
	}
}; unordered_set<pii, hash_> st;

signed main() {
	cin >> n >> k;
	int x = -1;
	rep(i,0,n) if (n - i >= k and (k + 1) * (n - i) >= i * k and !(((k + 1) * (n - i) + i * k) & 1)) x = i;
	cout << x << endl;
	cout << (x * k + (k + 1) * (n - x)) / 2 << '\n';
	rep(i,x+1,n) que.emplace(i, 0, 0);
	rep(i,1,x) rep(j,1,k) {
		auto tmp = que.top(); que.pop();
		cout << i << ' ' << tmp.id << '\n';
		que.emplace(tmp.id, tmp.val + 1, 0);
	} 
	while (que.top().val < k + 1) {
		el t1 = que.top(), t2; que.pop();
		vec.clear();
		while (1) {
			t2 = que.top(); que.pop();
			if (st.count({t1.id, t2.id})) vec.eb(t2);
			else { st.insert({t1.id, t2.id}); break; }
		} 
		cout << t1.id << ' ' << t2.id << '\n';
		for (auto v : vec) que.emplace(v);
		que.emplace(t1.id, t1.val + 1, 1), que.emplace(t2.id, t2.val + 1, 1);
	}
}

两道构造一道交互 就是这样的题解