2023-02-NOI 春测前考试记录
To say goodbye is to die a little.
2023-02-11
总之就是十分困难。
T1 签到 (checkin)
真·签到题
我是 fw ,甚至无法下手,感谢 @cqbzly 把我教会了。
首先定义
解释一下这个式子的意思,对于在
现在我们令
这样做的原因很简单,只有
然后把
然后要高精度。
T2 数据结构 (yyl)
傻逼卡常题小清新数据结构
卡常狗能不能去死啊。
需要一点直观的感受,加上合理的猜测。非常轻松地可以知道求这个带权重心如果用点分治之类的手段会让这道题变得非常不轻松。
于是大胆地在 dfn
序上找性质,直接从重心本身的定义出发,它的子树和之后是总和的一半,加上要求深度最小的,那么答案的子树和一定严格大于总和的一半,于是直接从这个 dfn
的带权中点出发往根上面跳就可以了,往上面跳是
然后这题卡常,必须写 fenwicktree
才能过。
T3 爆搜 (dfs)
我只会
据说可以 meet in the middle
拿到很多分,但是我考场没写出来,也没有人写出来。
2023-02-18
这 Day2 怎么比 Day1 简单?但是我是暴力选手,也就没什么影响了。
T1 博弈 (game)
我很会读题,讲真的
开始没反应过来,然后突破口在于你怎么让
然后就做完了,直接一边拓扑计算出从每个点开始能够走的最长路,做个背包就行了。
我是脑瘫,宏定义中用了位运算但是没加括号调了我
T2 排列 (perm)
我是暴力选手
第一眼,原题,这里指 AGC059C ,第二眼,这怎么还有
比较淡定,对着数据范围写了个 DAG
上 dp
,但是限制太多,不好去刻画。
想着去再拿
这个限制的转化很妙,先抄一遍原来的限制
然后这个玩意儿有传递性,任取一个
上面这个结论本该是我考试是暴力跑出来去验证的,但是我暴力挂了。。。
接下来一点做法上的小小震撼,因为合法前缀只有
最后的复杂度
T3 子段和 (seg)
我是暴力选手,麻烦我自己边写暴力边动脑子
一个比较明显的做法是直接二分最后的代价,贪心地去判合法就可以了,判断的过程写在正解前一步,整个过程的复杂度是
这样就可以拿到
思维上总是跳不出去,比较明显地正解的复杂度绝对与
本身来说我
然后正解把上面这个过程整体维护了,定义这个函数
这样就可以直接数据结构整体维护了,启发是在处理有变元的函数求和时,注意考察这个函数是否有整体的性质,能不能同时维护来消掉枚举该变元的复杂度。
2023-02-20
嗯?我怎么犯低级错误了?!就当是攒 rp 了吧。
T1 逃生 (escape)
我是脑瘫
但是好笑的是按
为什么想不到???我真的能想起来反悔贪心这个东西吗???
首先按照
T2 Match
ok 板题
T3 Edge
ok 原题
T4 羽未 (umi)
神题,膜拜 @wicton
还是犯了没有进一步思考的毛病。
赛时写了
其实这个做法已经非常接近正解了,但是还是因为没有想到用管辖区间的覆盖次数去刻画分段点,导致我每一次修改都需要重构一遍分段,这也是这个暴力的瓶颈。
刻画分段点其实非常简单,因为上面说过了,当没有管辖区间任何一个管辖区间同时覆盖
对于修改操作,无非就是某一个颜色的管辖区间的端点发生了改变,用 set
可以很轻松地维护。
最后就可以得到一个
代码比较难写,所以放一下。
umi.cpp
/* Stars in the sky, floating in darkness, soon I will fly faster than Light. See through my eyes, time standing down, onward to space, engines stand by. Sense loss of time Nebula’s blurring, lights flashing by Worlds Unknown. Imminent approach, sensors reacting, soon I’m through faster Than Light. Suddenly stop Readings come in, nothing in sight Sun glowing bright. */ #pragma GCC optimize("Ofast,no-stack-protector") #pragma GCC target("avx2,fma") #include <set> #include <cstdio> #include <iostream> #include <algorithm> #define LL long long #define ULL unsigned long long using namespace std; const int MAXN = 2e5 + 5; template <typename T> inline void read(T& x) { x = 0; int f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); } x *= f; } template <typename T, typename... Args> inline void read (T &x, Args&... Arg) { read (x), read (Arg...); } template <typename T> inline T Abs(T x) { return x < 0 ? -x : x; } template <typename T> inline T Max(T x, T y) { return x > y ? x : y; } template <typename T> inline T Min(T x, T y) { return x < y ? x : y; } int n, m, maxl, a[MAXN]; set<int> q[MAXN]; struct SegmentTree { struct Node { int l, r, ls, rs, sum, minl, maxl, tag; } s[MAXN << 2]; void build(int p, int l, int r) { s[p].l = l, s[p].r = r; if (l == r) return; int mid = (l + r) >> 1; build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r); } void push_up(int p) { s[p].minl = Min(s[p << 1].minl, s[p << 1 | 1].minl); s[p].maxl = Max(s[p << 1].maxl, s[p << 1 | 1].maxl); if (s[p << 1].minl < s[p << 1 | 1].minl) s[p].ls = s[p << 1].ls, s[p].sum = s[p << 1].sum, s[p].rs = Max(s[p << 1].rs, s[p << 1 | 1].maxl); if (s[p << 1].minl > s[p << 1 | 1].minl) s[p].rs = s[p << 1 | 1].rs, s[p].sum = s[p << 1 | 1].sum, s[p].ls = Max(s[p << 1 | 1].ls, s[p << 1].maxl); if (s[p << 1].minl == s[p << 1 | 1].minl) s[p].rs = s[p << 1 | 1].rs, s[p].ls = s[p << 1].ls, s[p].sum = s[p << 1].sum + s[p << 1 | 1].sum + Max(s[p << 1].rs, s[p << 1 | 1].ls); } void push_down(int p) { if (!s[p].tag) return; s[p << 1].minl += s[p].tag, s[p << 1 | 1].minl += s[p].tag; s[p << 1].tag += s[p].tag, s[p << 1 | 1].tag += s[p].tag; s[p].tag = 0; } void update1(int p, int l, int r, int k) { if (l > r) return; if (l <= s[p].l && s[p].r <= r) { s[p].minl += k, s[p].tag += k; return; } push_down(p); int mid = (s[p].l + s[p].r) >> 1; if (l <= mid) update1(p << 1, l, r, k); if (r > mid) update1(p << 1 | 1, l, r, k); push_up(p); } void update2(int p, int x, int k) { if (s[p].l == s[p].r) { s[p].maxl += k, s[p].ls += k; return; } push_down(p); int mid = (s[p].l + s[p].r) >> 1; if (x <= mid) update2(p << 1, x, k); if (x > mid) update2(p << 1 | 1, x, k); push_up(p); } void updateused(int col, int k) { int siz = q[col].size(); if (!siz) return; update1(1, *q[col].begin(), *(--q[col].end()) - 1, k); update2(1, *q[col].begin(), k * siz); } int query() { return n - s[1].sum - s[1].ls - s[1].rs; } } seg; int main() { freopen("umi.in", "r", stdin); freopen("umi.out", "w", stdout); read(n, m); for (int i = 1; i <= n; i++) read(a[i]), q[a[i]].insert(i), maxl = Max(maxl, a[i]); seg.build(1, 1, n); for (int i = 1; i <= maxl; i++) if (!q[i].empty()) seg.updateused(i, 1); printf("%d\n", seg.query()); for (int i = 1, x, y, col; i <= m; i++) { read(x, y), col = a[x]; seg.updateused(col, -1), q[col].erase(x); seg.updateused(col, +1), seg.updateused(y, -1); q[y].insert(x), seg.updateused(y, 1), a[x] = y; printf("%d\n", seg.query()); } return 0; }
2023-02-21
脑洞场,没什么好补的,就记录一下考场的思路与时间分配。
T1 expand
第一眼看成生成函数题了
流汗,真的流汗了,第一眼看成生成函数题了,甚至默写了一遍 Poly
。
但是我没有直接弄出组合意义,我是写了暴力才看出来的。
图就直接搬了,按照样例来解释一下。
可以看成从
T2 角谷猜想 (conj)
流汗题目
真的流汗,出题人是个什么玩意,我这辈子都做不出来这种题。
然后非常淡定,写了个 bitset
高精直接模拟,这段代码非常简单,可以贴一下。
conj.cpp
bitset<(int)5e4> add(bitset<(int)5e4> a, bitset<(int)5e4> b) { bitset<(int)5e4> c = a ^ b; bitset<(int)5e4> d = (a & b) << 1; if (d.count() == 0) return c; else return add(c, d); }
然后获得
本题做法较为多样,主体思路是把
分成若干个长度为 的块,然后基于块进
行操作。这里描述一下出题人的做法:
首先,我们将奇数的操作之后紧跟一次偶数的操作,把它们看成一次操作。那么
每次操作都会减少的一位(不过可能有进位)。
我们每轮选择一个,然后尝试进行 次操作,并求出 次之后剩余的
结果以及遇到奇数的次数。
如果操作次数较少,即,那么只有最后 位会被用到,我们直接截取最后
位,然后进行同样的操作即可。否则截取的效果不是特别好,我们将其分为两次
的操作,把第一次的结果给第二次进行计算即可。
考虑这一计算过程的时间复杂度。因为保证数据随机生成,所以我们可以认为遇
到奇数的次数大致为。而每次遇到奇数会使得结果乘上 ,因此每轮这样的操作之后,结果大小大致为 ,因此 每次会乘上 ,故在 轮后必定结束。再考虑每轮操作的复杂度。对于 ,有 ;对于 ,有 。
这个复杂度其实出题人不太会算,感性理解 + 打表表明总复杂度大约为级别的。
T3 宝藏 (treasure)
流汗题目
我要继续流汗了。暴力过了。
字面意思,暴力过了,没什么好说的,总之就是边加边判就能过。
T4 远古计算机 (computer)
全场唯一有意思的题目
subtask1
很简单,可以手动构造
node 1 read 0 a write a 0
subtask2
斐波拉契打表前 jmp
到对应行输出。
subtask3
最快的方式就是找
subtask4
考场没写完,以
subtask5
每次跑一遍都更新一遍被占用的状态,跑
2023-02-23
能不能不要把暴力写挂了啊喂 😭
cricle
没有第一时间想出来。。。
我甚至是在调完杜教筛之后才回来补的这道题,算是个签到,思路很简单,直接强连通缩点然后对于每一个连通块
young
%%% @wicton
非常自闭,我以为这个期望是破题口,可以找到一些性质,结果这就是一道技巧性的 dp
,以前没遇到过这样的模型,这波直接卡死了。
定义
问题在于这样怎么求最小生成树,我以前做过 XOR MST
,但是我根本没有转化过来。
根据第
位是否是 将所有点分成两个集合 , ,那么 和 内部一定形成最小生成树,于是我们在 和 之间找到一条最小的边将他们连起来就可以了。
上面这一段是题解,然而我一开始并不能很好的理解,更形象的理解方式是,建一棵 0/1 trie
,把所有的点权塞进去,对于一个节点,如果它又有左儿子,又有右儿子,那么则需要用一条最小的边将他们连起来。
用
我花了好长的时间才理解。。。
解释一下,首先枚举
然后考虑怎么去求
simple
才整完整道题,写题不要旷不要旷
好怪哦,这个限制,好怪哦,我再看一眼,再看一眼。 但是它既没有融化也没有爆炸
嗯,完全没有思路,那我就打个表,在我经过一系列的猜想与反复修正之后我发现一个串合法必须是它循环同构的所有串里面字典序最小的才满足,这个非常好想,手玩一下限制就发现是一个不断位移的过程,然后我发现
那就可以得到这个串的所有特征了,一个没有循环节且在所有循环同构的串里面字典序里最小的串。
显然我不会去刻画字典序最小,因为求出总数后除以长度就行了,问题是求没有循环节的串的个数,直接定义
想一下我们在求啥
然后这样直接上杜教筛。
naive
妙哦
这是个经典模型,然后我不会呗。。。
你要换个角度想,定义
2023-02-27
青鱼和序列
踩
我觉得这道题只被踩了
因为确实不该放在这个位置,说难也不难,但是一定不 noip
,在使用了操作二之后这个操作一和操作二都将变得等价,那么特判只是用操作一的情况就可以了。复杂度为
垃圾绑点。
青鱼和怪兽
我不会写代码
一眼二分,转移为
注意这个地方不能用
青鱼和区间
我我我我我。。。。。。我是不会讲题的
BOT
首先定义包含
那么我们考虑去计算不合法的方案数,定义
最后计算答案的时候用总数减去不合法的方案数即可。
2023-02-28
脑洞场了属于是
序列划分 dos
你好,我是不会 t1 的小菜鸡
sbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbssbsbsbsbsb。
确实脑瘫,非常脑瘫,如果
这大概是最关键的一步了。接下来就可以反复地把
当
二进制的世界 binary
你好,我是只会暴力的小菜鸡
这道题就更神奇了,我们可以发现所有的数值域在
安排座位 seat
你好,我是会做小丑题的小菜鸡
唯一会做的题,我哭死。
非常简单啊,直接容斥,式子非常简单
最小生成树 mst
你好,我是会背模板的小菜鸡
直接默写动态最小生成树,花了我 1.5h
,然后被卡常到 85pts
,因为出题人认为这个做法非常丑陋,没有用到题目的性质。
从部分分开始思考,对于一条链,把
然后回到原题,Kruskal
建树之后拉出几条等效链,合并即可。
2023-03-01
挂,都可以挂
失落的世界
失落的
50pts
我真的服了,好吧,我自己的问题。
二分是显然的,但是问题就在于你只能在
看了题解,其实很简单,我们可以倒着模拟一遍这个过程,从大往小反复横跳,处理出来起点和方向即可。
谜域的界外
纯纯靠猜
因为是指数,张的飞快,我们处理出来小于
聚合的塔尖
聚合的错误
非常下饭,能把初始化写错的也没谁了。
能不能想清楚啊!!!1,在去重的时候每个点只因该保留 pair<int, int>
保留一个,否则最后达不到
正解是对询问分块,因为最大值超过
这启示我们什么,在不好下手的时候应该从问题的规模考虑分治入手。
沉眠的回声
不会,咕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通