成都集训-test0717

今天的模拟赛太逆天了。 \(\text{NOIP}\) 模拟赛一紫三黑。

得分: \(100+14+0+14=128\) ,被吊打。

T1 珠宝

题目描述

\(n\) 个物品,每一个物品有一个空间 \(w_i\) 和一个价值 \(v_i\)

你有一个空间为 \(i\) 的背包,问最多可以装下多少价值的物品。

问价值 \(i \leqslant K\) 时的每一个答案。

数据范围\(n \leqslant 1e6\) , $K \leqslant 5e4 $ , \(w \leqslant 300\)

思路点拨

我们按照一般的 \(\text{01}\) 背包的思路,本题可以做到 \(O(nk)\) ,严重超时。

我们比较一般的背包问题的数据规模和本题的数据规模,本题的物品数量十分庞大,但是物品的空间很小。

我们从物品的空间下手,从 \(1\)\(300\) 枚举一个空间 \(V\) 。对于每一个空间时 \(V\) 的物品打包考虑。

首先,我们可以将这些物品价值降序排序,因为我们在同样的空间下总是会先选价值大的。

接下来,我们对这些物品做前缀和,保存到数组 \(g_{i \times V}\) 中,表示选择 \(i\) 个空间为 \(V\) 的物品的价值。

转移的时候,我们发现,两个状态 \(f_{i}\)\(f_{j}\) 会互相影响仅当 \(i \mod V = j \mod V\)

所以我们枚举我们要转移的状态 \(\mod V\) 的余数,假设他为 \(z\)

那么我们将形如 \(f_{i \times V+z}\) 的状态放在一起考虑,得到转移方程:

\[f_{i \times V+z} = \max \{ f_{i \times V+z} , \max \{ f_{j\times V+z} + g_{(i-j)\times V}\} \} \]

这个式子对我们的时间没有任何的优化,但是 \(g\) 函数的增长率时单调不递增的。证明略。

所以我们的决策点有单调性,使用分治或者单调队列上二分均可通过。本题还算是比较可做的。

T2 [JOI 2020 Final] 火事

题目描述

给定一个长为 \(N\) 的序列 \(S_i\),刚开始为时刻 \(0\)

定义 \(t\) 时刻第 \(i\) 个数为 \(S_i(t)\),那么:

\[\left\{ \begin{array}{ll} S_0(t)=0\\S_i(0)=S_i\\S_i(t)=\max\{S_{i-1}(t-1),S_i(t-1)\} \end{array} \right.\]

你将对 \(Q\) 个操作进行评估,第 \(j\) 个操作让时刻 \(T_j\) 时的区间 \([L_j,R_j]\) 全部变为 \(0\)

执行一个操作需要一定的代价,执行第 \(j\) 个操作需要以下的代价:

\[\sum\limits_{k=L_j}^{R_j}S_k(T_j) \]

求每个操作需要的代价。

注意:每个操作都是独立的。

思路点拨

考虑 \(S_{i}(t)=\max_{j=i-t}^{i}\{a_i\}\) ,所以我们可以获得 \(O(n^2)\) 的暴力。

但是,当我们把 \(S_i(t)\) 画出来之后,我们发现对于每一个数的贡献十分的有规律,呈现出一个平行四边形。例如:

对于一个节点,我们定义 \(L_i\) 为左边第一个 大于 它的数的下标, \(R_i\) 为右边第一个 大于等于 它的数的下标。

这个平行四边形的顶点分别是 \((i,0),(i,i-L_i-1),(R_i-1,R_i-i-1),(R_i-1,R_i-L_i-2)\)

那么每一次询问就是问一个线段 \([l,r]\) 在纵坐标为 \(t\) 的时候所经过的点的权值和。这个显然是可以差分简化问题的。

我们可以考虑将一个个平行四边形拆成若干个有规律的部分,使得可以更加方便计算。

一种十分简单的想法就是把一个平行四边形按照横坐标拆成一个个竖线,这样很好处理,但是全部的竖线数量过多。

我们注意到,如果按照纵坐标可以划分成一条条斜线。按照横坐标可以划分成 \(R_i-i\) 个竖线,按照纵坐标可以划分成 \(i-L_i\) 条斜线。

如何保证线的数量有限,可以利用笛卡尔树一个广为人知的结论 \(\sum_{i} \min\{i-L_i,R_i-i\}\)\(O(n \log n)\) 级别的。给出一个证明:

这个式子相当于询问笛卡尔树的每一个节点的左右儿子子树的最小值之和。

我们可以把 \(\min\) 换一种理解方式,把 \(i\) 有左右两个儿子变换成将 \(i\) 的两个儿子合并。这样取 \(\min\) 就可以变换成启发式合并。

每一个节点至多被合并 \(\log n\) 次,所以总体是 \(O(n \log n)\) 的。

接下来,我们对于横线和斜线分别考虑。

横线十分简单,将全部询问差分后离线下来就可以扫描线,比较无脑。

斜线不好搞,对于一群在同一条斜线上的点 \((x,y)\) ,我们发现随着 \(x\) 加上一个 \(1\)\(y\) 也会加上一个 \(1\) 。也就是说,同一条斜线上的点 \(x-y\) 是一个定值。

我们将原平面直角坐标系的点 \((x,y)\) 变换成 \((x-y,y)\) 就可以将斜线转换成横线。因为在同一斜线上的点 \(x-y\) 是定值。

对于斜线转换后的横线,我们可以如法炮制同样操作。在维护扫描线的时候,考虑到本题时限比较紧张,使用 \(\text{BIT}\) 维护。

时间复杂度 \(O(n \log ^2 n)\)

T3 Mousetrap

题目描述

有一个有 \(n\) 个房间和 \(n-1\) 条走廊的迷宫,保证任意两个房间可以通过走廊互相到达,换句话说,这个迷宫的结构是一棵树。

一个老鼠被放进了迷宫,迷宫的管理者决定和老鼠做个游戏。

一开始,有一个房间被放置了陷阱,老鼠出现在另一个房间。老鼠可以通过走廊到达别的房间,但是会弄脏它经过的走廊。老鼠不愿意通过脏的走廊。

每个时刻,管理者可以进行一次操作:堵住一条走廊使得老鼠不能通过,或者擦干净一条走廊使得老鼠可以通过。然后老鼠会通过一条干净的并且没被堵住的走廊到达另一个房间。只有在没有这样的走廊的情况下,老鼠才不会动。一开始所有走廊都是干净的。管理者不能疏通已经被堵住的走廊。

现在管理者希望通过尽量少的操作将老鼠赶到有陷阱的房间,而老鼠则希望管理者的操作数尽量多。请计算双方都采取最优策略的情况下管理者需要的操作数量。

注意:管理者可以选择在一些时刻不操作。

对于所有的数据,\(1 \le n \le 10^6\)

思路点拨

题目意思比较复杂,所以使用了更为清晰的原题面。

我们为了简化问题,我们将陷阱房作为数的根,这样老鼠就尽量远离根。

我们考虑这只倒霉的老鼠会怎么走。它会一头栽进一个子树然后被自己弄脏的路径困住。

那么在此时,我们伟大的管理员就可以把所有要封死的路径给堵住,最后把老鼠的路径擦干净。

我们先看看在一颗子树中,管理员的操作吧。我们定义 \(f_i\) 表述老鼠从 \(i\) 子树,和管理员斗智斗勇之后被堵在叶子然后回到 \(i\) 管理员的最小步数。

我们考虑类似于数学归纳法的方式求出这个 \(f_i\) 。也就是,在求 \(f_i\) 的时候,我们知道 \(i\) 的全部儿子的 \(f_i\)

那么如果管理员无动于衷,老鼠会干什么?肯定会选一个 \(f_i\) 最大的儿子的子树钻进去,以此拖延时间。

管理员此时是有一个步骤的,所以他可以把这个最大的 \(f_i\) 堵住,老鼠就会走第二大的。有:

\[f_{i} = \text{2nd_max} \{f_{son}\}+\text{child} \]

\(\text{child}\) 是儿子的个数。为什么是加上 \(\text{child}\) 呢?

这是因为,我们的老鼠会钻进一个第二大 \(f_{i}\) 的子树,那么其他 \(\text{child-1}\) 个子树的边我们肯定是要堵上的,最后还要帮老师擦干净一条边。

为什么子树内的边要堵上,有没有可能不堵边更优秀呢?不可能,因为老鼠钻进这条边就至少要擦一条边让他出来。不如花一条边堵上,多一事不如少一事。

对于 \(f_i\) 而言,老鼠钻进了子树后被堵在的一个叶子,我们此时需要把 \(fa_i\) 到陷阱房之间不要的岔路堵上,这个我们记录为 \(g_{fa_i}\)

$g_{i} $怎么求呢?我们考虑从父亲继承。先放出方程:

\[g_{i}=g_{fa_i}+\text{child}-[i\neq st] \]

其中 \(st\) 是起点。如果 \(i\) 不是起点,那么我从起点到达 \(i\) 的时候就会弄脏一条边,我们只需要擦掉 \(\text{child}-1\) 条别的边就可以了。

但是,老鼠一开始不一定会往自己的子树钻,有可能会走到别个子树钻进去。这是十分复杂的。

注意到答案可以二分,我们对于一个值 \(\mid\) ,判断是否可以处理。我们模拟老鼠的每一个决策,给出代码(有注释):

bool lis[MAXN];//在s到t的路径上 
bool check(int step){
	int sum=0;//管理先手
	for(int x=s;x!=t;x=dad[x]){
		sum++;//我多一步
		int ned=0;//这是我需要的步数
		for(int i=0;i<e[x].size();i++){
			int to=e[x][i];
			if(lis[to]) continue;
			if(f[to]+g[x]<=step) continue;
			if(!sum) return 0;//步数不够,管理员速度不行
			sum--;//少了一步 
			ned++;//这是要堵上的 
		}
		step-=ned;
		if(step<0) return 0;
	} 
	return 1;
}

总体时间复杂度 \(O(n \log V)\)

这题太逆天了!

T4 小丑

题目描述

给定一张 \(n\) 个点, \(m\) 条边的无向图。有 \(q\) 次询问,每次询问给出一个区间 \([l,r]\) 问在删去这个区间的边后图是否是二分图。

思路点拨

我们发现删除操作十分的恶心,考虑转换成添加操作。我们可以将边的数组开两倍,对于一次删除操作 \([l,r]\) :

image

我们可以转换成判断 \([r+1,l'-1]\) 是不是二分图。可以想到,对于每一个 \(r\) ,我们找到一个最小的 \(pos_r\) 使得 \([r+1,pos_r]\) 不是二分图。

怎么求解 \(pos\) ?我们先考虑单个 \(l\) ,这个 \(pos_l\) 显然是可以二分的。我们二分一个 \(pos_r\) 使用染色法或者并查集判断是不是二分图。时间复杂度 \(O(nq\log n)\)

其实,$pos $ 数组具有单调性,所以我们可以使用分治法优化这个二分的过程。

发现在分治的过程中,对于每一个分治的段,我们都需要花费大量时间计算并查集。但是,这个并查集可以从分治树的父亲处继承一部分。维护可持久化并查集或者可撤销并查集即可。

时间复杂度 \(O(n \log ^2 n)\)

posted @ 2023-07-17 17:16  Diavolo-Kuang  阅读(38)  评论(0编辑  收藏  举报