NOI2020简要解题报告
NOI2020简要解题报告
身为美食家的我常常制作菜品,有一天在超现实树上翻修道路,意识到是蒟蒻的我的命运是成为时代的眼泪。
D1T1 美食家
现场得分
100pts : 141 人
95pts : 18 人
≥ 70pts : 202 人
≥ 40pts : 315 人
< 20pts : 24 人
算法:矩阵乘法
一句话题意:给出\(n\)个点\(m\)条边的有向图(不存在重边自环),点有点权 \(c[x]\) ,边有长度 \(w\) ,还有 \(k\) 次美食节使得在 \(t_k\) 时刻城市 \(x_i\) 的价值增大 \(y_i\),求从1号节点出发 \(T\) 天后回到1号节点的路线点权和最大值。
\(T\leq 5\)
直接暴力模拟。
特殊性质A(是一个环)
只可能存在一种合法的旅行方案,可以特判掉。
\(T\leq 52501\)
直接模拟不行了,需要dp。
设 \(f[i][x]\) 表示在第 \(i\) 天走到节点 \(x\) 的最大价值,转移的时候枚举 \(x\) 的出边 \((x,y,w)\) 用 \(f[i][x]+c[x]\) 更新 \(f[i+w][y]\),如果有美食节再加上美食节的价值。
时间复杂度: \(O(T(n+m))\)
\(k=0\)
因为\(T\) 的范围达到了惊人的1e9,直接dp行不通。根据平常的经验,复杂度与T相关的因子应该是 \(logT\),结合 \(n,m\) 的数据范围很小,可以想到用矩阵乘法优化dp。
先考虑不存在美食节的情况。
按照原来的dp从 \(f[i]\) 转移到 \(f[i+w]\) 很难处理,数据范围 \(w\leq 5\) 提醒我们可以拆边简化矩阵。
具体来说,设原来的边是 \((x,y,w)\) ,把它拆成 \((x,x_1,0), (x_1,x_2,0) …… ,(x_w-1,y,c[x])\)(特别的,\(w=1\) 不需要拆边),\(x_i\) 为新建的虚点。
至于广义矩乘的正确性就不再证明了,矩乘的细节也不再赘述,注意第一天的城市1也是产生贡献的。
答案就是 \(f[1]Mat^T\)(Mat为矩阵)
时间复杂度:\(O((n+4m)^3logT)\)
\(k<=10\)
现在需要考虑美食节的额外贡献,这个是不容易在矩阵中维护的。
考虑到只有最多10个美食节,就把它们的时间排序,枚举两次相邻美食节的时间差,对 \(Mat\) 做 \(log (t_i+1-t_i)\) 次的矩阵快速幂。
直接把这个美食节的贡献加在做好矩乘后的 \(f\) 的对应位置上即可,显然正确。
时间复杂度:\(O(k(n+4m)^3logT)\)
\(m\leq 50\)
\(k\) 最大会是200,直接按上面的方法做时间复杂度是错的,考虑预处理 \(Mat\) 的2的整次幂,每次对 \((t_i+1-t_i)\) 二进制拆分后把 \(f\) 与对应 \(Mat\) 的幂相乘即可。
因为 \(f\) 是一个行向量,所以矩乘的时间复杂度是 \(O((n+4m)^2)\);
时间复杂度:预处理 \(O((n+4m)^3logT)\) 运算 \(O(klogT(n+4m)^2)\)
正解
\(m\) 最大可以是500,前面的思路肯定不会错,所以在矩阵大小上进行优化。
不难发现,拆边复杂度与 \(m\) 相关,想办法换个等效的方式使得与 \(m\) 无关,也就是与 \(n\) 相关,可以想到拆点。
具体的,把每个点拆成 \(x_1,x_2,x_3,x_4,x_5\),对于原来的边 \((x,y,w)\),拆成 \((x_1,x_2,0),(x_2,x_3,0),……(x_w-2,x_w-1,0),(x_w-1,y,c[x])\)
这样 \(Mat\) 的大小都变成了 \(5n\)。
沿用上一个部分分的做法。
时间复杂度:预处理 \(O((5n)^3logT)\) 运算 \(O(klogT(5n)^2)\)
D1T2 命运
现场得分
100pts : 70 人
≥ 72pts : 96 人
≥ 48pts : 148 人
≥ 40pts : 203 人
< 16pts : 29 人
算法:dp,线段树合并
一句话题意:见题面。
\(m\leq 16\)
限制的数量很少,考虑容斥,限制数量是偶数的方案容进来,奇数斥出去。
考虑方案数即为选中限制的路径并的边全赋值成0、其他边随意的方案数。
可以用树剖或虚树维护。
时间复杂度:\(O(2^mmlog^2n)\)
\(m\leq 22\)
也是容斥,据说有 \(O(2^mm)\) 的复杂度,我还不会,会了补上。
前两个解法与正解关系不大。(毕竟m最大是5e5)
\(n\leq 2e3\)
首先考虑一个东西:对于一个节点 \(u\) ,所有限制 \((u,v)\) 中深度最深的那个限制被满足了,其他的限制也一定满足。证明显然。
设 \(lim[u]\) 表示节点 \(i\) 的最深限制,\(dep[u]\) 为节点 \(u\) 的深度(特别的,\(dep[1]=1\))
建立dp模型:树形结构要求我们尽量用树形dp,而树形dp里面下传上(儿子传父亲)是处理最为方便的,因此考虑维护每个节点的子树信息。
设 \(f[x][i]\) 表示所有 \(u\) 在以 \(x\) 为根的子树内的限制中,未被满足的限制最深深度是 \(i\) 的合法方案数。
特别的 \(f[x][0]\) 表示子树内所有限制都满足的方案。显然,答案为 \(f[1][0]\)
考虑dp细节:
叶子节点:\(f[x][lim[x]]=1\)
非叶节点:枚举孩子 \(v\)
1 .\((x,y)=1\) 则 \(f[x][i]=\sum_{j=0}^{dep[x]}f[x][i]f[y][j]\)
2 .\((x,y)=0\) 则 \(f[x][i]=\sum_{j=0}^if[x][i]f[y][j]+\sum_{j=0}^{i-1}f[x][j]f[y][i]\)
整理就是 \(f[x][i]=\sum_{j=0}^{dep[x]}f[x][i]f[y][j]+\sum_{j=0}^if[x][i]f[y][j]+\sum_{j=0}^{i-1}f[x][j]f[y][i]\)
直接做复杂度 \(O(n^3)\)。
设前缀和 \(g[x][i]=\sum_{j=0}^{i}f[x][j]\)
dp式变为 \(f[x][i]=f[x][i]g[y][dep[x]]+f[x][i]g[y][i]+g[x][i-1]f[y][i]\)
可以优化到 \(O(n^2)\)
\(\min(n,m)\leq 2e3\)
可以用虚树优化到 \(O(n\min(n,m))\)
正解
既然 \(O(n^2)\) 过不掉,正解的复杂度应该就是 \(O(nlogn)\)。考虑用数据结构优化
把dp式变换个形式:\(f[x][i]=f[x][i](g[y][dep[x]]+g[y][i])+g[x][i-1]f[y][i]\)
这个过程可以用线段树合并完成。因为在合并的过程中需要同步记录原来的前缀和,所以采用先递归左子树再递归右子树的方式合并。
据说线段树合并优化的这部分也叫整体dp
时间复杂度:\(O(nlogn)\)
D1T3 时代的眼泪
现场得分
100pts : 2 人
≥ 72pts : 10 人
≥ 64pts : 31 人
≥ 48pts : 116 人
≥ 24pts : 332 人
0pts : 21 人
算法:分块,容斥(解法不唯一)
一句话题意:给出一个长度为 \(n\) 的排列,\(m\) 次询问求下标在 \(r_{i,1},r_{i,2}\),值域在 \(c_{i,1},c_{i,2}\) 的顺序对数目。
\(n\leq 5e3\)
暴力\(O(mnlogn)\)。
特殊性质A(无值域限制)
就是[Ynoi2019 模拟赛] Yuno loves sqrt technology I的裸题。
具体实现的话也是分块+容斥,不过Ynoi这题时空卡的很紧,理解思路足够了,不建议去实现。
主要的思想就是分块后贡献拆成五部分:
1 .整块内部 2 .散块内部 3 .左右散块之间 4 .散块对整块 5 .整块对整块
1,2,5可以预处理 \(O(1)\) ,3需要做 \(O(\sqrt n)\) 归并,4在预处理后可以 \(O(\sqrt n)\) 枚举查询。
具体归并就是先把序列分块排序,然后把指定范围内的元素按顺序加入,维护双指针即可。
时间复杂度:\(O(n\sqrt n)\)
特殊性质B(任意一对限制要么互相包含,要么互相分离)
看到一些代码是直接暴力做的,但是我自己造了一组符合性质B的极端数据都跑不动,仔细分析这档数据最坏区间长度和是 \(O(n^2)\) ,还没想到靠谱的做法。
特殊性质C (最多50个逆序对)
也就是说最多只有50个数对不会产生贡献,先默认所有数对都是顺序对,再减去逆序对即可。
具体的,先求出来这50个逆序对是谁,方法的话可以线段树也可以cdq分治,然后把每个询问按照全是顺序对去计算,可以用可持久化线段树去做,最后枚举这50个逆序对,看那个询问包含了他们,减去即可。
时间复杂度:\(O(mlogn+50m)\)
正解
特殊性质C的做法很难拓展(逆序对最多是 \(O(n^2)\) 的),所以考虑沿用特殊性质A的做法,也就是分块+容斥。
还是考虑转化成5部分的贡献:
1 .整块内部 2 .散块内部 3 .左右散块之间 4 .散块对整块 5 .整块对整块
记号约定:
\(B\) 块长
\(b[i]\) 当前块排序后第 \(i\) 个位置(有fi=权值 se=下标)
\(L[i],R[i]\) 第 \(i\) 个块的左端点,右端点。
\(pos[i]\) 第 \(i\) 个位置的所属块。
\(rk[i][j]\) 第 \(i\) 块排名为 \(j\) 的数。
\(f[i][j]\) 第 \(i\) 个位置到所在块左端点排名小于等于 \(j\) 的数的个数
\(pre[i][j]\) 前 \(i\) 个块小于等于 \(j\) 的数的个数
\(low[i][j]\) 第 \(i\) 块小于等于 \(j\) 的数的最大排名
预处理不赘述
下文中 \(l1,r1\) 是下标区间 \(l2,r2\) 是值域区间。
1 .整块内部
记 \(h[i][j][k]\) 表示第 \(i\) 个块排名 \(j\) 到排名 \(k\) 的答案。
只需要 \(O(\frac n B)\) 扫一遍整块,每块的答案为 \(h[i][low[i][l2-1]+1][low[i][r2]]\)
考虑预处理:借用 \(f\) 数组,容斥一下得到 \(h[i][j][k]=h[i][j][k-1]+f[b[k+L[i]-1].se][k-1]-f[b[k+L[i]-1].se][j-1]\)
2 .散块内部
\(O(B)\) 扫一遍,对于在值域内的元素,累加上 \(f[i][low[pos[i]][a[i]-1]]-f[i][low[pos[i]][l2-1]]-(f[l1-1][low[pos[i]][a[i]-1]]-f[l1-1][low[pos[i]][l2-1]])\)。
注意 \(l1\) 是块边界的特判。
3 .左右散块之间
沿用特殊性质A的归并,\(O(\sqrt n)\)。
4 .散块对整块
\(O(B)\) 扫一遍,左块对于在值域内的元素,累加上 \(pre[pos[r1]-1][r2]-pre[pos[r1]-1][a[i]]-(pre[pos[l1]][r2]-pre[pos[l1]][a[i]])\)。
右块做法一样,把值域改成小于即可。
5 .整块对整块
设 \(g[i][j][k]\) 表示第 \(i\) 个块前 \(k\) 个排名的数与前 \(j\) 个块产生的答案。
预处理:借用 \(pre\) 数组,\(g[i][j][k]=g[i][j][k-1]+pre[j][rk[i][k]]\)
\(O(\frac n B)\) 从左到右扫描。
对于当前块累加 \(g[i][i-1][low[i][r2]]-g[i][pos[l1]][low[i][r2]]-(g[i][i-1][low[i][l2-1]]-g[i][pos[l1]][low[i][l2-1]])\)
因为要减去值域在 \(l2-1\) 内的数产生的贡献,同步记录一个 \(cnt\) 表示这个块之前小于 \(l2-1\) 的数的个数。
每次减去 \(cnt*(low[i][r2]-low[i][l2-1])\) 即可。
复杂度分析:\(O(nlogn+nB+mB+m\frac n B)\),把 \(B\) 设置为 \(\sqrt n\) 即可,视实现常数可能要调整。
整体时间复杂度: \(O(n\sqrt n)\)。分块做法常数较大,需要卡常。
额外的思考
从来没有只有一个解法的数据结构题。
本篇的算法空间复杂度是 \(O(n\sqrt n)\),还好原题的空间限制是 1GB,实测空间峰值是600MB左右。
孪生题[Ynoi2019] Happy Sugar Life空间限制只有 128MB,需要 \(O(nlogn)\) 甚至是线性的空间复杂度,本篇的做法显然过不掉,还有更优秀的算法。
本题还有高维莫队,树套树,平面分治等做法,思路和实现相对分块比较难。
另外,特殊性质A有 \(O(n^{1.41})\) 的最优理论算法。
D2T1制作菜品
现场得分
100pts : 53 人
≥ 70pts : 92 人
≥ 45pts : 144 人
≥ 20pts : 235 人
< 15pts : 65 人
算法:贪心,构造,背包,bitset
一句话题意:给出 \(n\) 种原料,质量为 \(d_i\) 克,要做 \(m\) 道菜,每道菜都需要原材料 \(k\) 克,每道菜最多选2种原材料,保证 \(n\) 种原材料质量和 \(\sum_{i=1}^nd_i=mk\),存在分配方案则输出任意一种,不存在输出-1。
\(n,m\leq4,k\leq50\)
排序后直接特判就好。嫌麻烦的话实测暴搜也是可以的。
\(m=n-1\)
此时至少有1道菜需要用两种原材料,不妨调整一下,使得每道菜都用到了两种原来料,这个显然是能做到的。
直觉告诉我们用当前量最少的原材料搭配最多的可以构造出合法解。下面证明这个直觉是对的。
把 \(d\) 从小到达排序,题目要求 \(\sum_{i=1}^nd_i=mk=(n-1)k\)。
引理1:\(d_1<k\)
证明:假设 \(d_1\geq k\),那么 \(d_2,……,d_n\geq k\) ,则 \(\sum_{i=1}^nd_i\geq nk>(n-1)k\),与题目要求矛盾。
引理2:当 \(n>2\) 时,\(d_1+d_n> k\)
证明:假设 \(d_1+d_n\leq k\),即 \(d_n\leq k-d_1\),则 \(\sum_{i=1}^nd_i\leq d_1+(n-1)(k-d_1)=(n-1)k-(n-2)d_1<(n-1)k\),与题目要求矛盾。
结合上述引理,每次把最少的原材料 \(d_1\) 拿完,用最多的原材料补到 \(k\),最多的原材料一定还有剩余,再放入原来的序列中维护大小关系,每次 \(n\) 和 \(m\) 同时减少1,知直到 \(n=2,m=1\) 直接把两种原材料全拿完,就成功构造出了一个合法解。
维护序列用什么都行,建议用std::set比较方便。
时间复杂度:\(O(mlogn)\)
\(m\geq n\)
考虑怎么样把这个情况转化到 \(m=n-1\) 的情况。直觉告诉我们把最多的原材料分配给这多出来的几道菜能构造出合法解。下文证明这个直觉也是对的。
引理3:\(d_n\geq k\)
证明:假设 \(d_n<k\),则 \(\sum_{i=1}^nd_i< nk\leq mk\),与题目要求矛盾。
所以每次用 \(d_n\) 单独做一道菜,这种原材料还有剩余,\(n\) 不变,\(m\) 每次减少1,直到 \(m=n-1\) ,再套用上一问的解法,就能构造出合法解。
仍然用std::set维护就好。
时间复杂度:\(O(mlogn)\)
\(n\leq 50,n-2\leq m\leq 5e3,k\leq 500\)
出现了 \(m=n-2\) 的情况,意味着可能无解了。
手推小数据发现:\(n=3\) 无解,\(n=4\)可以拆分成两个 \(m=1,n=2\) 的子问题,\(n=5\)可以拆分成 \(m=1,n=2\) 和 \(m=2,n=3\) 的子问题……
直觉告诉我们能把这种情况转化为两个 \(m=n-1\) 的子问题就能构造出合法解,否则一定无解。下面证明这个直觉还是对的……(直觉解题法确信)
引理4: 合法解存在当且仅当可以选出一个下标集合 \(S\) 使得 \(\sum_{i\in S}d_i=(|S|-1)k\)
证明:
充分性:
对于 \(S\) 关于全集的补集 \(S'\) 也满足 \(\sum_{i\in S'}d_i=(|S'|-1)k\),前面的引理1,2已经保证了 \(m=n-1\) 的情况一定存在合法解。
(证明了当前解集是全解集的子集)
必要性:
仍然要求如果均衡一下使得分配方案每道菜都分到两种原材料,这是可以做到的。假想1到 \(n\) 号节点构成了一张图,如果一道菜用了 \(u\) 和 \(v\) 两种原材料,就连一条边 \((u,v)\)。
图中只有 \(n-2\) 条边,所以这个图至少能划分成两个部分,而又因为每个点至少有一个出边,容易发现这个图只会是两棵树(即\(m=n-1\)的情况),其他的图的形态总能找到一个方法从当前图的形态转化过去。
(证明了全解集是当前解集的子集)
问题就转化成了怎么找到这样的一个集合 \(S\)。
把式子中的 \(|S|\) 移到右边,就变成了 \(\sum_{i\in S}(d_i-k)=-k\)。
就变成了0/1背包问题。
直接做的话就是枚举当前原材料选还是不选,枚举值域加上当前原材料的贡献,对于每个值维护bitset。
时间复杂度:\(O(\frac {n^3k} {w})\) 空间复杂度:\(O(\frac {n^2k} {w})\)
正解
正着优化不了就反着来。反过来维护前 \(i\) 个数能组合出的值有哪些。
空间复杂度不变,但是时间复杂度降到了 \(O(\frac {n^2k} {w})\) 。
D2T2 超现实树
100pts : 22 人
≥ 40pts : 80 人
≥ 20pts : 150 人
< 12pts : 121 人
算法:?
一点也简化不了题意
\(maxh\leq 1\)
所有的答案都是Almost Complete。
\(maxh\leq 2\)
只有包含以下三棵树的答案才是Almost Complete,特判一下。
tree1:
2
2 0
0 0
tree2:
2
0 2
0 0
tree3:
3
2 3
0 0
0 0
\(maxh\leq 5\)
枚举一下所有深度为 \(maxh\) 的树,判断是否能生成,所有都能生成答案是Almost Complete,否则是No。
但是没有找到对应的代码,我自己编了一个做法:枚举每个树能生成的不超过 \(maxh\) 的所有数,按照二叉树二倍标号的法则用bitset维护是否能被生成(或者可以状压)。
出题人说时间复杂度是 \(O(2^{2^{maxh}})\),但我编的做法复杂度最坏似乎是 \(O(\frac {m*2^{maxh}} {maxh})\),而且实现非常麻烦,与正解关系不是太大就咕咕咕了(轻喷)。
\(maxh\leq 10\)
定义链树为一条树链加上若干个单个节点,也就是说对于链树上的非叶节点要么只有一个孩子,要么至少一个孩子是叶子,即 \(min(size_{lson},size_{rson})\leq 1\)(显然只有一个节点的树也是链树)。
比如下图就是一棵链树:

容易发现所有高度为 \(h\) 的链树都能被生成时所有高度为 \(h\) 的二叉树都能被生成。证明考虑把一棵非链树树每次选两个同父亲的叶子删去直到不能找到这样的两个叶子,最后一定剩下来一棵链树。
并且链树具有性质:任何非链树都不能生成链树,证明显然,非链树的枝杈只会越长越多。
相当于把所有高度为 \(h\) 的二叉树看做一个线性空间的话,所有链树构成了一组基。
再考虑一个性质:高度为 \(h\) 的所有链树可以生成所有高度大于 \(h\) 的链树。
所以只要所有高度为 \(h\) 的链树能生成这个集合就是Almost Complete(即只有有限棵链树不能被生成)。
把上一问改成枚举所有高度为 \(maxh\) 的链树即可。出题人说时间复杂度是 \(O(2^{2*maxh})\) ,但也是非常麻烦,咕咕咕(轻喷)。
(特殊性质没找到稳定解法,出题人的ppt里面也没细讲。个人猜想是留给正解写挂的?)
正解
再考虑一个性质:所有集合中不是链树的树都是无用的。可以用链树不能由非链树生成证明。
也就是只考虑集合中的链树就可以得到这个集合的答案。于是可以把所有非链树都剔除掉。
考虑把集合中所有链树合并起来。对于每个节点有四种状态:
1.无左孩子,有右孩子
2.无右孩子,有左孩子
3.两个孩子都有且左孩子是叶子
4.两个孩子都有且右孩子是叶子
考虑对于这个节点只有四种状态都存在这个节点的子问题是Almost Complete,否则就会有无限棵树不能生成。
按照这四种情况编码,相当于合并后形成了一棵四叉树。
根据前面的性质,容易得到只有这棵四叉树是完备的这个集合才是Almost Complete,其中完备指对于每个非叶节点四个孩子都存在。可以画图理解。
做法就是构造出这棵四叉树直接深搜判断即可。
据出题人分析四叉树的平均深度不超过 \(O(\sqrt n)\),证明很复杂。
D2T3 翻修道路
45pts : 31 人
≥ 30pts : 51 人
≥ 20pts : 128 人
≤ 5pts : 200+ 人
(我太菜了,这道题只会做20分,未来也许能补上)
算法:弦图
一句话题意:给出一张边带权的弦图,指定两点 \(s,t\) ,求出最短的从 \(s\) 到 \(t\) 的路径长度使得删掉这条路径后图仍然连通。
特殊性质A(所有边的边权相同)
相当于求出来一条经过边数最少的路径。
先放出结论,后面给出证明。
求出\(s,t\)的最短路后,如果删掉这条路径不合法,则无解,否则这条路径就是题目所求
无论有无特殊性质,这条路径一定无环,证明显然。
考虑所有可能的边割集,这里边割集指删去其中的所有边后原图划分为两个集合。
假设 \(u,v,w\) 是三个在路径上连续的点,且 \(u\) 和 \(v\) 属于不同的集合。
考虑 \(w\) 的位置。
1 .如果 \(v,w\) 在同一集合,那么这个割集就已经走完了 (只有一条边 \((u,v)\)) 显然是不合法的。
2 .如果 \(u,w\) 在同一个集合,那么 \(u,w\) 必然有边直接相连,可以用反证法证明。
对于最短路讨论:
1 .如果这条路径上穿过了大小为1的割集,显然无解。
2 .否则,一定可以把形如 \((u,v,w)\) 的边改成 \((u,w)\)。
结论得证。
具体的做法就是记录最短路的方案,用并查集维护连通性就好了。

浙公网安备 33010602011771号