2024.10

2024.10

10.2 模拟赛 T4

先离散化用类似白雪皑皑的方式用并查集跳着修改区间。

考虑一个部分贪心,假设区间都不包含,修改一定是修改连续的一段区间。发现每次最小权值一定是最靠左的最小的区间。只需维护这样的最小值修改即可。

10.3 模拟赛 T4

考虑初步转化,打表发现最优策略是个幌子,零钱数就是钱数模 5 。

又发现用零钱买东西的都是傻子,用多少还多少,对答案没有贡献。不如把整钱和零钱拆开对应贡献和体积,就转化为了一个 01 背包。但是体积和贡献不是一个量级的东西,对体积 dp 空间根本开不下。遂交换体积贡献求最小体积得到了一个 \(O(n^2)\) 做法。

考虑优化,按贡献分 4 组逐层转移。对于一层,设 \(g(x)=f(i,u+xi)\) ,从 \(g(y)\)\(g(x)\) 加上了一个前缀和,前缀和显然有四边形不等式的性质,可以用 cdq 从上一层转移的过程中维护最优决策。这就优化到了 \(O(n\log n)\)

CF1009F

定义 \(f(x,k)\) 表示以 \(x\) 的子树中距离 \(\le k\) 的点数,套路写出 dp 式:

\[f(x,k)=\sum_{y\in son(x)} f(y,k-1) + 1 \]

有好想好写的线段树合并写法,把相对距离变成绝对距离,时空复杂度都为 \(O(n\log n)\)

还有更有前途的长链剖分写法, \(f(x,k)\)\(f(to(x),k-1)\) 继承,每把优先算完重儿子的答案再加上轻儿子的贡献统计答案。看起来很像 DSU on Tree ,但是不同的是长链剖分不用清空贡献,而且长链剖分的贡献继承是链依赖的。

然后空间复杂度是 \(O(n^2)\) ,但是有很多重复的空间,用指针给每个链申请 \(O(链长)\) 的空间就能将空间复杂度降为 \(O(n)\)

还有隔壁机房 T4 也能使用这个做法:统计所有距离 \(\le k\) 的点对的异或的和。

一个做法是将点权按位拆掉,记 0/1 为 黑/白 ,统计所有距离 \(\le k\) 的颜色不相同的点对。用点分治维护一下,找到重心以后就是找距离 \(\le k\) 的点对,开两个桶维护访问到的点中距离根节点的距离为 \(k\) 的 黑点白点个数,每次扫一遍子树统计答案,再扫一遍扔到桶里。时间复杂度为 \(O(n \log^2 n)\)

另一个做法是用长链剖分,和上面一样拆贡献,设 \(f(x,k,0/1)\) 是以 \(x\) 为根的子树中距离 \(\le k\) 的点中颜色为 0/1 的点数,转移:

\[f(x,k,0/1)=\sum_{y\in son(x)}f(y,k-1,0/1) \]

和上面一样优化转移、空间优化。统计答案时强制钦定经过 \(x\) ,扫一遍统计答案再扫一遍扔到桶里。时间复杂度为 \(O(n\log^2n)\) ,空间复杂度为 $O(n) $ 。

怎么和官方题解不一样?貌似这个拆贡献比原题解扔到 dp 式子里更好理解。

qoj 8235

求 mex 不好求,反过来我们看 \([1,mex-1]\) 的点是否 \(k\) 邻域。

点权互不相同,一个 trick 是把点权排序然后让点权 \(\le w\) 的点强制出现,这个东西可以二分。

然后我们只需找到一种快速的方式找到一个前缀的点中到 \(x\) 的最大距离。

然后我们自然的想到直径的性质。距离 \(x\) 最远的点一定在直径的两端。然后就可以直接判断了。

本题卡常数,可以使用 ST_table + 欧拉序 维护树上的 LCA 。

P6623 [省选联考 2020 A 卷] 树

拆贡献。把每个点的贡献变为 \(a(x)+dep(x)\) 。每次求答案就是给一个子树中的点权减去 \(dep(x)\) 再异或起来。

套路按位考虑。先考虑再二进制下没有进位操作。直接对每一位维护一个桶,用 dsu on tree 每次把贡献加起来。

把儿子的答案先异或起来。我们发现点到 \(x\) 的距离每次加 1,我们只用考虑 +1 后会进位的情况。我们考虑一个位 \(t\) 进位,给运算 \(\%2^t\) 统计,等价于我们要找这个:

\[a(x)-dep(x)-1\equiv 2^t-1\pmod {2^t} \]

就是:

\[a(x)\equiv dep(x) \pmod {2^t} \]

我们沿用上面的 dsu on tree 的桶维护就好了。

P6961 [NEERC2017] Journey from Petersburg to Moscow

一个直接的想法是对 \(\ge val\) 的边权减去 \(val\) ,剩下的全部赋值为 0 。最后给答案加上 \(K\times val\) ,但是我们无法保证这一定是对的。

但是我们似乎找不到更好的做法,考虑证明它是对的。

  1. \(val\) 恰好为第 \(k\) 大的边,那么这是对的。

  2. \(val\) 比第 \(k\) 大的边小,那么我们一定没有把所有除了前 \(k\) 大的边全赋值为 0 ,所以这样的答案比原来大。

  3. \(val\) 比第 \(k\) 大的边大,前面的贡献没变,后面的边权反而少减去了一些边权,所以答案还是变大了。

所以我们直接暴力枚举 \(val\) ,全局取 min 就是答案。

P10080 [GDKOI2024 提高组] 匹配

题面打直球,一个直接的想法是乱搞费用流,但是并不对。我们考虑先找到一组最大匹配,然后通过调整找到一组答案。

我们先跑一遍 dinic ,如果它直接满足条件,那么我们直接输出它。.

否则我们在残量网络上考虑这个问题,这就等价于我们要找一个环,让原来的匹配改道,这等价于找一个边权异或和为 1 的环。

我们同时维护栈里面的元素和访问过的元素就能做到 \(O(n+m)\) 找环。

CF1204E

先考虑 dp 算一些答案然后再统计起来。我们先设 \(f(i)\) 为最大前缀和为 \(i\) 的方案数。

这个东西太难直接统计了,不如把条件放松为最大前缀和 \(\le i\) 的方案数。然后套路把 1 和 -1 变为向量 \((1,1)\)\((1,-1)\) 转为网格图计数。然后我们再考虑 \(f(i)\) 再网格图的意义是什么。

这个东西等价于从 \((0,0)\)\((n+m,n-m)\) 的路径中和 \(y=i\) 相交的个数。我们像 卡特兰数 一样将 \((n+m,n-m)\)\(y=i\) 反转统计答案即可。

后面差分为 \(f(i)\)\(\sum f(i)\) 就是答案。

ABC214H

首先发现如果一些点在一个强连通分量当中,那么这个强连通分量的权值都会加到贡献当中。于是我们先把它缩点变为 DAG 再来考虑问题。

然后这个东西很像蓝书上面的有线电视网络,然后我们考虑用费用流维护这个东西。

我们套路拆点,拆成入点和出点,中间连两条边,一条 \((x,y,1,val)\) ,一条 \((x,y,K,0)\) ,分别表示第一次走和后面在走,剩下原 DAG 上的正常连 \((x,y,K,0)\),表示随便走没贡献。

再考虑起点和终点的情况,连 \((S,1,K,0)\)\((x,T,K,0)\) 表示只能从 1 开始和可以从任意位置结束。

再来考虑时间复杂度, \(O(nmk)\) 根本过不了,考虑建图优化这个过程。

我们把贡献反过来,求 \(sum\times K-maxflow\) 。由于这个图是 DAG ,从 \(x\)\(y\) 必然无法经过 \([x+1,y-1]\) 的点,所以贡献为 \(sum_{y-1}-sum_x\)

对于每个点,连边 \((x,y,1,0)\)\((x,y,K,val)\) ,就是吧贡献反过来。

对于起点连边 \((S,1,K,sum_{s-1})\) ,终点连边 \((x,T,K,sum-sum_x)\)

但是这样做边数没有变少,但是我们发现负边权变少了。于是我们考虑对 SPFA 做神奇优化。用优先队列优化,但是我们不强制要求每个点只能入堆一次,再剪剪枝就过了。

qoj 7177

首先要找到所有的环,找环通常是用生成树或者 dfs ,我们发现 dfs 的本质实际上是一个非树边的过程,所以不如直接用生成树来找。

我们先考虑如何构成环,环一定由树上的路径和若干非树边构成。于是我们能得到一个错误的算法:我们只考虑一条非树边构成的环,把他们 gcd 起来。

但这是显然错误的,这个算法没有考虑一些由很多条非树边的环。我们考虑怎么让这些环也被统计到答案。

Trick 1 :一个复杂环(由大于 1 条非树边构成的环)一定是由若干个简单环(一条非树边构成的环)合并得到的。

我们考虑反证法,一个复杂环不由简单环只可能是两条非树边连接了两个连通块。但是我们是生成树。两个连通块一定没有边相连,否则再执行生成树算法的过程中就会将它们联通。

Trick 2 :由 更相减损术 我们知道 \(\gcd(x,y)=\gcd(x,y-x)=\gcd(x,y+x)\) ,所以我们只用将若干个环的交集加入集合中即可。

Trick 1 我们知道一个复杂环 \(C\) 可以通过若干个简单环 \(c_i\) 合并得到。它一定能写成 \((\sum c_i)-2\times(\sum c_i\cap c_j)\) 的形式。所以最后的答案一定是所有 环长 和 两倍简单环的交 的最大公约数。

最后我们只剩下一个问题,就是如何求出两个简单环的交。

我们考虑容斥,设两条非树边是 \(<x_1,y_1>\)\(<x_2,y_2>\) ,两条路径 $x_1\to y_1 $ 和 \(x_1\to y_2\) 与另外两条路径 \(x_1\to y_2\)\(x_2\to y_1\) 一定有一个经过了环的交,一个恰好没有。

所以:

\[2\times len(c_i\cap c_j)=|((d(x_1,y_1)+d(x_2,y_2))-(d(x_1,y_2)+d(x_2,y_1))| \]

使用欧拉序倍增求 LCA ,时间复杂度为 \(O(M^2)\)

qoj 7749

首先想想边权是什么,设 \(w(x)\)\(x\) 的质因子个数,边权一定为 \(w(x)+w(y)-w(\gcd(x,y))\)

然后我们想想边界。 首先令 \(L=1\) ,答案一定是 \(\sum w(i)\)

我们考虑贪心,先连一些边权是 0 的边,让它们成为若干个联通块,这个东西可以用线性筛筛出来。

然后我们贪心的选一些边。我们按 \(w(i)\) 从小到大考虑一些联通块,然后我们考虑一些 \(w(j)\)\(w(i)\) 倍数的连通块,这样做贡献不会增加,边权是 \(w(i)\)

但是这样做不能解决所有的问题。如果,没有 \(w(i)\) 互为倍数的边,上面的做法就做不了了。于是我们考虑这种情况,发现这种情况一定夹在两个质数中间,这中间的数很少,暴力 prim 即可。

qoj 9409

考虑用线段树维护这个东西。

我们直接做肯定无法维护每次修改后所有给定区间是否合法。但是我们发现一个区间的红球数是随操作次数单调不升的。这个红球数为 1 很有启发性,我们可以考虑每次 check 一个区间的红球数是否为 1 。

我们考虑使用懒标记往下更新答案。

我们使用 线段树 将每个区间拆成 \(O(\log N)\) 段,然后我们发现 一个段 \(\ge2\) 红球是等价的,我们可以等到一个段的红球数降为 1 时更新所有这个段的所有覆盖它的区间的答案。因为一个答案合法一定有一个段的红球数为 1 。

然后每个区间最多只会被更新信息 \(O(\log N)\) 次,所以时间复杂度为 \(O(N \log N)\)

CF2030E

我们先考虑一个子序列的贡献如何快速计算。先把它们映射到一个数组上。设 \(c(i)\) 为值为 \(i\) 的元素的个数。我们发现一个子序列个贡献是 \(\min(c(0))+\min(c(0),c(1))+\cdots +\min(c(0),c(1),\dots,c(n-1))\)

然后我们考虑用 dp 计算这个东西,考虑 \(i\) 填几个。发现当前 \(\min(c(0),c(1),\dots,c(i))\) 同样也很重要,设它们为 \(j\) ,所以 \(dp(i,j)\) 为填了前 \(i\) 位, \(\min(c(0),c(1),\dots,c(i))=j\) 的子序列数。

我们考虑两种情况:

  1. 当前填的数大于等于 \(j\) , 所以 \(dp(i,j)=dp(i-1,j)\times \sum_{k=j}^{c(i)}C_{c(i)}^k\)

  2. 当前填的数小于 \(j\) ,所以 \(dp(i,j)=C_{c(i)}^j\times\sum_{k=j+1}^ndp(i-1,k)\)

后面随便填,所以答案是 \(dp(i,j)\times j\times 2^{c(i+1)+c(i+1)+\cdots+c(n-1)}\)

精细实现 \(dp(i,j)\) 的状态总数是 \(O(n)\) 的,并且转移暴力实现也是 \(O(n)\) 的。

可以对 \(dp(i,j)\) 使用滚动数组优化,也可以用 std::vector\(O(n)\) 的空间。

CF1603D

\(dp(i,j)\) 位前 \(i\) 个数填了 \(j\) 段的答案。容易写出 dp 式:

\[dp(i,j)=\min_{k=1}^{i} \{dp(k-1,j-1)+c(k,i)\} \]

先化一下 \(c(k,i)\) 的式子,由题:

\[c(l,r)=\sum^{r}_{i=l}\sum^{r}_{j=i} [\gcd(i,j)\ge l] \]

\[c(l,r)=\sum^r_{k=l}\sum^{r}_{i=l}\sum^{r}_{j=i} [\gcd(i,j)==k] \]

\[c(l,r)=\sum^r_{k=l}\sum^{\left \lfloor \frac{r}{k} \right \rfloor }_{i=\left \lceil \frac{l}{k} \right \rceil }\sum^{\left \lfloor \frac{r}{k} \right \rfloor }_{j=i} [\gcd(i,j)==1] \]

交换枚举顺序:

\[c(l,r)=\sum^r_{k=l}\sum^{\left \lfloor \frac{r}{k} \right \rfloor }_{j=1}\sum^{j }_{i=1} [\gcd(i,j)==1] \]

后面是 欧拉函数 的定义,设 \(sphi(n)=\sum_{i=1}^nphi(i)\)

所以 \(c(l,r)=\sum_{k=l}^r s(\left \lfloor \frac{r}{k} \right \rfloor )\) 每次数论分块解决。

这个东西貌似无法拆贡献,让我们想想它有什么性质,考虑它是否满足 四边形不等式

我们对于 \(l_1<r_1<l_2<r_2\) ,我们发现有 \(c(l_1,r_1)+c(l_2,r_2)\le c(l_1,r_2)+c(l_2,r_1)\) 恒成立。所以这个动态规划是 1D1D决策单调性优化

我们可以用 cdq 分治解决这个问题。

CF360E

考虑贪心,我们直接做不好做,可以考虑先找到一种方案,然后经过调整让它合法。

显然,这个范围是幌子,我们每条边只会设为 \(L(i)\)\(R(i)\) 。不如我们先让边权最大,在调整每条边的边权。

想想我们什么时候能将一条边权设为 \(L(i)\) ,如果 \(d(s_1\to x)<d(s_2\to x)\) 那么这条边一定对答案没有影响。我们直接调整即可。由于 \(k\) 很小,所以我们每次暴力找到这样的一条边调整边权就可以做到 \(O(nk\log n)\)

最后我们只需判断 \(d(s_1\to t)\)\(d(s_2\to t)\) 的大小即可。

qoj 8542

将问题反过来,每次前后缀转化为相减。考虑多少次操作可以将一个 \(a\) 直接减为 0 。这个贡献一定为 \(\sum a(i)\)

我们再考虑如何将不合法的 \(a\) 转化为合法的 \(a\) 。如果我们 “减过头” 了,这相当与让 \(a(i)\) 加 1 。同样的,总贡献也会加 1 。

我们再想想合法的 \(a\) 有什么性质。我们发现如果 \(a(i)>a(i+1)\) 那么一定减一个前缀,否则一定减一个后缀。

我们把两边贡献合起来,先令 \(a(0)=a(n+1)=+\inf\) ,这个序列合法当且仅当 \(\sum|a(i)-a(i+1)|\le 2\times \inf\)

这是一个差分形式。考虑一个区间的贡献。

每次如果我们让一个区间加 1,上面的式子减去 2,对答案的贡献是区间长度。我们用 笛卡尔树维护这样的区间,再贪心从小到大选择区间直到满足条件。

posted @ 2024-10-04 18:42  lichenyu_ac  阅读(42)  评论(0)    收藏  举报