国集神犇果然还是国集神犇,我见得太少了QwQ
# Part1. 纪
第一次打 NOI,离省队比较遥远只能打同步赛。
CCF的管理员开小差去了,同步赛延时了十多分钟才开,刚打开又没有题面,有了题面又没有大样例……
还是先把题目从头到尾看完了,感觉到应该是按照难度排序的,所以先攻第一题。看到 $T$ 极大而 $n$ 极小就想到大概要矩阵加速,然后就按照之前的思路从(我能想到的)最暴力的DP开始优化。f[t][u][i]
表示第 $t$ 天时,还有 $i$ 天到达点 $u$ 的最大美味值,暂时没有考虑美食节。
然后想了20min没有优化出来,回来看题目才看到 $t$ 只有 $5$,一时心头生艹,就开始码代码。写着写着想起还有美食节,看时间还比较可控,就又停下想美食节。于是就联想到 NOI Online 的一道几乎一样的题——把DP过程分段;回忆那道题的解题思路,很快就把 $O(tn^3\log n)$ 优化到 $O(tn^2\log n)$ 了,调程序花了 30min……
跑大样例感觉有点卡常,最后回头再加了些 register
之类的东西。
然后T2想了10min感觉“不可做”,然后就想骗分。暴力显然就不管它,然后看 $m$ 很小就想要状压,推式子发现要或卷积,整个人都懵了,最后写了个最水的暴力走人……
T3看到题目的时候笑了一发(这个出题人在造梗)。初步整理思路肯定要扫描线,然后写了一个主席树来处理矩阵求和,把暴力分得了。一开始读题没有看到每列有且只有一个点……后面看到了才知道之前想的莫队复杂度是对的 QwQ 时间不太多了,最后莫队常数巨大跑不出来……
Day1 打完 13:30(虽然CCF延长了15min),然而多校赛 12:00 开始……就去打多校了,感觉聚聚都去NOI了,这一场多校的中学格外的少。
Day2什么东西,不想写……
# Part2. 解&补
- D1T1. 美食家(delicacy)
先从暴力DP考虑,要处理一下某个时刻处于一条边上还没有走完的情况,$f_{t,u,i}$ 表示时刻 $t$ 正在前往 $u$,还剩 $i$ 天到达 $u$(如果 $i=0$,则表示已经到了 $u$)。
转移是:
因为一条边花费的时间 $t_e$ 只有 $5$,我们发现第三维只有 $0\sim4$ 的取值,所以干脆把一个点 $u$ 按时间拆成 $u_0,u_1,\dots,u_4$,$5n$ 仍然很小,可以支持矩阵加速。
然后就是处理美食节。注意到只有美食节当天的转移会发生变化,于是可以把美食节作为阶段的划分点,一个阶段内的转移方程是不变的,可以矩阵加速。
此时复杂度是 $O((5n)^3\log nk)$($k$ 为美食节数量),过不了。但是在 NOI Online 里面有一个一模一样的操作——$O(n^3\log n)$ 预处理转移矩阵的 $2^0,2^1,2^2\dots$ 次幂,做转移时可以用储存当前状态的单列矩阵做 $\log n$ 次乘法,这样做一次复杂度是 $O((5n)^2\log n)$ 的,于是优化到 $O((5n^3)\log n+(5n)^2k\log n)$ 可过。
- D1T2. 命运(destiny)
方法很多,可以容斥,也有不容斥的做法。我采用的是不容斥的。
> Tab. 下文直接说成“黑白边染色”了,即要求给定路径上要有黑边。
先考虑 DP。对于一条边定义其深度为较深的一个端点的深度,则先预处理出每个点 $u$ 作为路径的下端点时,至少在多深必须要染黑边。比较显然,如果有多条路径的下端点是 $u$,则上端点的深度取 $\max$,记这个值为 $pre_u$(若 $u$ 不是路径下端点,则 $pre_u=0$)。另外记 $dep_u$ 表示 $u$ 的深度。
根据这个限制进行黑白染色:定义DP状态 $f_{u,d}$ 表示节点 $u$ 上方的第一条黑边的深度是 $d$ 的合法方案数。
从子树 $v$ 到当前节点 $u$ 的转移只需考虑边 $(u,v)$ 是否染黑,即:
又要求 $u$ 满足作为 $pre_u$ 的限制,上式的 $d\in[pre_u,dep_u]$。实际代码实现时,我们可以把 $d\in[pre_u,dep_u]$ 的 $f_{u,d}$ 的初值赋为 $1$,其余为 $0$。
这样就得到了一个 $O(n^2)$ 的 DP,接下来要用到线段树合并进行优化——
我们发现之前DP进行的操作:
- 赋初值:区间修改(区间加);
- $(f_{v,d}+f_{v,dep_v})$:区间加;
- $\prod(f_{v,d}+f_{v,dep_v})$:按位乘。
于是线段树需要支持这些操作。
可以维护一个 pair
(代码中为 DATA
)作为懒标记 $(a,b)$,其意义为 $x\times (a,b)=ax+b$。
这样的话区间加容易处理,按位乘需要线段树合并——一直递归到线段树的叶子节点(注意因为是动态开点,所以叶子节点不一定是缩成一个点的线段),然后乘法合并。懒标记的乘法合并直接推导一下就行了。
$v$ 子树内的点在参与合并后就没用了,可以 restore 一下节省空间。
- D2T1. 制作菜品(dish)
> Linked 另外写了一篇博客
# 源代码
- D1T1 delicacy.cpp
1 | /*Lucky_Glass*/ |
- D1T2 destiny.cpp
1 | /*Lucky_Glass*/ |
THE END
Thanks for reading!
> Linked 零和 Zero-Sum-Bilibili