闲话 23.3.28

闲话

实验材料

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

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

模拟赛

翻了 但是完全没翻!

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

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 个定能组成一个最优子结构,剩下的节点按大小分,看是自成一组还是加入前面的一堆节点。但是好像比较难写。
所以写了个乱搞作法。首先承认这样一个结论:最终的节点度数一定只有 kk+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×n 的正方形网格,用三种颜色染色,求有多少种染色方案使得至少一行或一列是同一种颜色。结果对 998244353 取模。

n106

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

i0j0(1)i+j+1(ni)(nj)f(i,j)

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

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

i0(1)i+1(ni)3i+n(ni)

i,j 都不是 0 时,答案就是

i1j1(1)i+j+1(ni)(nj)3(ni)(nj)+1

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

i1j1(1)i+j+1(ni)(nj)3(ni)(nj)+1= k1i+j=k(1)k+1(ni)(nj)3n2kn+ij+1= k1(1)k+13n2kn+1i+j=k(ni)(nj)3ij= k1(1)k+13n2kn+1i+j=k(ni)(nj)3(i+j2)(i2)(j2)= k1(1)k+13n2kn+1+(i+j2)i+j=k(ni)3(i2)×(nj)3(j2)

中间的一步写开就是 ij=(i+j2)(i2)(j2)

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

Submission.

posted @   joke3579  阅读(171)  评论(13编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示