闲话 23.3.28

闲话

实验材料

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

约 4.5 小时后更新:
好 实验完了
果然写一些有争议话题会引发讨论 以及随之而来的关注
这篇闲话在推流前就拿到了 5-6 倍于平常的阅览
我记得之前有人在夏征写了当时争议挺大的新分级系统
然后拿了第一名
似乎这被称作“抖机灵”的样子

模拟赛

翻了 但是完全没翻!

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

两道构造一道交互 就是这样的模拟赛

杂题

CF997C

给定一个 \(n \times n\) 的正方形网格,用三种颜色染色,求有多少种染色方案使得至少一行或一列是同一种颜色。结果对 \(998244353\) 取模。

\(n\le 10^6\)

看到这题就想到容斥。假设 \(f(i, j)\) 是至少 \(i\)\(j\) 列是同一种颜色的方案数,答案就是

\[\sum_{i\ge 0}\sum_{j\ge 0} (-1)^{i + j + 1} \binom{n}{i}\binom{n}{j} f(i, j) \]

讨论一下 \(f\)
注意到若 \(i, j\)\(> 0\) 则同色的行列只有可能都是一种颜色。因此枚举颜色和随意涂色的位置可以得到 \(f(i, j) = 3\times 3^{(n - i)(n - j)}\)
反之,我们只需要考虑行或列的情况,而 \(f(i, 0)\)\(f(0, i)\) 同构,我们只需要讨论一种,这里讨论 \(f(i, 0)\)\(i\) 行彼此无影响,并有 \(n - i\) 行随意涂色,枚举颜色和随意涂色的位置可以得到 \(f(i, 0) = f(0, i) = 3^i \times 3^{n(n - i)}\)
由于需要容斥统计贡献,我们钦定 \(f(0, 0) = 0\),也就是这种情况没贡献。

注意到 \(f\) 存在两种不同的取值,我们不妨分开求解。
\(i, j\) 存在 \(0\) 时可以平凡 \(O(n)\) 计算,这部分的答案就是

\[\sum_{i\ge 0} (-1)^{i + 1} \binom{n}{i} 3^{i + n(n - i)} \]

\(i, j\) 都不是 \(0\) 时,答案就是

\[\sum_{i\ge 1}\sum_{j\ge 1} (-1)^{i + j + 1} \binom{n}{i}\binom{n}{j} 3^{(n - i)(n - j) + 1} \]

我们不妨枚举 \(k = i + j\),将式子写成卷积形式:

\[\begin{aligned} & \sum_{i\ge 1}\sum_{j\ge 1} (-1)^{i + j + 1} \binom{n}{i}\binom{n}{j} 3^{(n - i)(n - j) + 1} \\ = \ & \sum_{k\ge 1} \sum_{i + j = k} (-1)^{k + 1} \binom{n}{i}\binom{n}{j} 3^{n^2 - kn + ij + 1} \\ = \ & \sum_{k\ge 1} (-1)^{k + 1} 3^{n^2 - kn + 1} \sum_{i + j = k}\binom{n}{i}\binom{n}{j} 3^{ij} \\ = \ & \sum_{k\ge 1} (-1)^{k + 1} 3^{n^2 - kn + 1} \sum_{i + j = k}\binom{n}{i}\binom{n}{j} 3^{\binom{i + j}{2} - \binom{i}{2} - \binom{j}{2}} \\ = \ & \sum_{k\ge 1} (-1)^{k + 1} 3^{n^2 - kn + 1 + \binom{i + j}{2}} \sum_{i + j = k}\binom{n}{i} 3^{-\binom{i}{2}} \times \binom{n}{j} 3^{- \binom{j}{2}} \end{aligned}\]

中间的一步写开就是 \(ij = \dbinom{i + j}{2} -\dbinom{i}{2} - \dbinom{j}{2}\)

可以多项式乘法优化求解。总时间复杂度 \(O(n\log n)\)

Submission.

posted @ 2023-03-28 15:16  joke3579  阅读(168)  评论(13编辑  收藏  举报