Logo zrl123456 的博客

博客

标签
暂无

Red-Blue Operations (Hard Version) 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-01-05 17:43:13

现在才发现已经一个半月没写题解了。

Red-Blue Operations (Hard Version)

题目考察:二分,前缀和(极值),贪心,排序。
题目简述:
给你一个序列 $\{a_n\}$,还有一个初始值均为 $0$ 的序列 $\{b_n\}$,有 $q$ 次询问,每次询问若有 $k$ 次操作,对于第 $i$ 次操作($i\in[1,k]$),选择一个 $j\in[1,n]$,使得 $a_j$ 加上 $(1-num_j\bmod 2)\times i$ 后再将 $num_j$ 加 $1$,最后求 $\displaystyle\min_{i=1}^na_i$ 的最大值。
数据范围:

  • $1\le n,q\le 2\times 10^5$
  • $\forall i\in[1,n],1\le a_i\le 10^9$
  • $1\le k\le 10^9$

显然,一个数被操作奇数次才可能被加。

注释 $1$:对于 $\{x_{2k}\}$,$\forall i\in[1,2k),x_i<x_{i+1}$,则 $\forall i\in[1,k],x_{2k-1}-x_{2k}<0$,其和显然小于 $0$。

所以说,为了让 $x_{2k-1}-x_{2k}$ 更大,我们让 $x_{2k-1}-x_{2k}=-1$,即对一个数的操作一定是一个区间。

考虑二分答案,二分数组最小值 $x$。在二分前将序列升序排序,以便我们快速找到小于数组最小值的数,这件事我们可以用二分实现。
二分找到小于该数的的数目 $sum$ 后,贪心的想,我们肯定要把最后一次操作作用在第一个数上,如果这样,我们只能对他做 $1$ 次操作。

注释 $2$:如果我们对其做 $3$ 次操作,那么贡献为 $k-(k-1)+(k-2)=k-1<k$。

那么这样的话,$\forall i\in[1,sum]$,$a_i$ 将会变为 $a_i+(k-i+1)$,如果 $\displaystyle\min_{i=1}^{sum}a_i+k-i+1<x$,则无法达到目标。

注释 $3$:对于 $a_i+k-i+1$ 我们可以将 $k$ 提出,在一开始(排序后!排序后!排序后!)对 $a_i-i+1$ 做前缀最小值,在 check 函数内再将 $k$ 加回。

这时还剩 $k-sum$ 次操作,在前面的 $sum$ 个数上应该会有一些数比 $x$ 要大,那么在那些数上进行 $2$ 次操作,使该数减 $1$。

注释 $4$:若所有的数都比要求的数要小($sum=n$)且剩余操作数为奇数,则需要撤销一个数的操作凑成偶数,但实际无法撤销任何一个数,故无法达到要求。

注释 $5$:若 $num_i=\sum_{j=1}^ia_i+k-i+1$,则在这些数上的操作数为 $2(num_{sum}-sum\times x)$,维护 $num_i$ 的方法类比注释 $3$。

设还剩 $lft=k-sum-2(num_{sum}-sum\times x)$ 次操作,那么剩下的就要在 $i\in[sum+1,n]$ 的 $a_i$ 上操作了。

注释 $6$:$lft\le 0$,直接判定可达到要求即可。

注释 $7$:若剩余一些操作($lft>0$)且无剩余数($sum=n$),则判定无法达到要求。

若 $lft\bmod 2=1$,则在一个数上操作一定会增加,证明请类比注释 $1$。
否则因为奇数加奇数为偶数,那么如果剩余两个数($n-sum\ge 2$)则在两个数上分别操作奇数次,证明仍然类比注释 $1$。
否则只剩 $1$ 个数,判定 $\displaystyle a_n-\frac{lft}{2}$ 是否大于等于 $x$ 即可。

代码

CF598(EDU1) 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-07-13 22:05:20

显然这篇题解是给自己看的,代码不贴。

A. Tricky Sum:

考察:数学。
题目简述:
$t$ 组数据,每组数据给定 $n$,求: $$\sum_{i=1}^n(-1)^{[i\in\{x|x=2^k,k\in\mathbb N\}]}i$$ 数据范围:

  • $1\le t\le 100$
  • $1\le n\le 10^9$

上边那个式子长的太不像人样了,所以我们给它简化一下:
$$\begin{aligned}\sum_{i=1}^n(-1)^{[i\in\{x|x=2^k,k\in\mathbb N\}]}i&=\sum_{i=1}^ni-2[i\in\{x|x=2^k,k\in\mathbb N\}]i\&=(\sum_{i=1}^ni)-2(\sum_{i=1}^{\lfloor\log_2n\rfloor}2^i)\&=\frac{n(n+1)}{2}-2^{\lfloor\log_2n+2\rfloor}+2\end{aligned}$$ 照着式子直接算,时间复杂度为 $\Theta(t\log n)$。

B. Queries on a String:

考察:字符串。
题目简述:
给你一个由小写字母构成的字符串 $s$,后面有 $m$ 次修改,第 $i$ 次修改给定 $l_i,r_i,k_i$,表示执行一下操作 $k_i$ 次:

  • $\forall j\in[l_i,r_i],s_{(j-l_i)\bmod(r_i-l_i)+l_i+1}\leftarrow s_j$

最后输出字符串 $s$。
数据范围:

  • $1\le|s|\le 10^4$
  • $1\le m\le 300$
  • $\forall i\in[1,m],1\le l_i\le r_i\le |s|$
  • $\forall i\in[1,m],1\le k_i\le 10^6$

首先容易想到在区间 $[l,r]$ 执行该操作 $k$ 次结果会如下:

  • $\forall j\in[l,r],s_{(j+k-1-l)\bmod(r-l)+l+1}\leftarrow s_j$

然后好像就没有了,注意一些实现上的小细节就行了,时间复杂度为 $\Theta(|s|m)$。

C. Nearest vectors:

考察:线性代数,精度优化。
题目简述:
有 $n$ 条射线在同一平面直角坐标系内,第 $i$ 条射线过原点和点 $(x_i,y_i)$ 并以原点为端点,求射线两两之间非优夹角(自己定义的:度数小于 180° 的夹角)最小的一组,若有多组任意输出满足条件的一组。
数据范围:

  • $1\le n\le 10^5$
  • $\forall i\in[1,n],(x_i,y_i)\in\complement_{\{(x,y)|-10^4<=x<=10^4,-10^4<=y<=10^4,x\in \mathbb Z,y\in \mathbb Z\}}\{(0,0)\}$

如图,这里有 $7$ 条射线:

我们注意到,若要求非优夹角要小,则两条射线一定要相邻,则我们在一开始要排序。
那么我们以什么为标准排序呢?
注意到我们可以根据原点外的那一点求出这条射线与射线 $\{(x,y)|x\ge 0,y=0\}$ 之间形成的上夹角(有超过一半在 x 轴上方或度数等于 0° 的夹角),通过 atan2 函数即可实现。

注意这里不能用 atan,精度会炸。

排完序后,我们对射线间非优夹角进行计算即可,时间复杂度为 $\Theta(n\log n)$。

D. Igor In the Museum:

考察:DFS。
题目简述:
给你一个 $n\times m$ 的网格图,由 .* 组成,有 $k$ 次询问,第 $i$ 次询问给定 $x_i,y_i$,求网格 $x_i,y_i$ 所在的 . 四联通块的与 * 相邻的部位数。
这里的部位不同当且仅当这个 * 位置不同或这个与 * 相邻的 . 的位置不同。
数据范围:

  • $3\le n\le1000,3\le m\le 1000$
  • $1\le k\le\min(nm,10^5)$
  • $\forall i\in[1,k],1\le x_i\le n,1\le y_i\le m$

由于 $k$ 较大,故考虑预处理,我们需要对于每个 . 均摊 $\Theta(1)$ 地求出每个 . 所在连通块与 * 相邻的部位数。
套路题,对于每个连通块,先求出它与 * 相邻的部位数,再依次将这个值赋给连通块中的每个网格。
求值和赋值都可以用 DFS 实现,最后对于每个询问 $\Theta(1)$ 回答即可,时间复杂度为 $\Theta(nm+q)$。

E. Chocolate Bar:

考察:dp。
题目简述:
$t$ 组数据,每组数据有一个 $n\times m$ 的网格图水平摆放,你能操作若干次,每次操作水平或竖直中的仅一个方向沿网格边缘将一个网格图分成两半,所切割长度为 $p$,同时切完的网格图成为了两个独立的网格图,设每次操作的代价为 $p^2$,问能凑出 $k$ 个网格的最小代价。(这简述相当严谨了)
数据范围:

  • $1\le t\le 40910$
  • $1\le n\le 30,1\le m\le 30$
  • $1\le k\le\min(nm,50)$

同 D,观察到 $t$ 的值域较大,而 $n,m,k$ 的值域都较小,考虑预处理。
设 $f_{a,b,c}$ 为在 $a\times b$ 的网格图中通过操作凑出 $c$ 个网格的最小代价,暴力按题意推式子:
$$f_{a,b,c}=\begin{cases}0&c\in\{0,ab\}\+\infty&c>ab\\min(\min_{x=1}^{\lfloor\frac{a}{2}\rfloor}(\min_{y=0}^c(f_{x,b,y}+f_{a-x,b,c-y}+b^2)),\min_{x=1}^{\lfloor\frac{b}{2}\rfloor})(\min_{y=0}^c(f_{a,x,y}+f_{a,b-x,c-y}+a^2))&\text{otherwise}\end{cases}$$ 容易发现最后时间复杂度为 $\Theta(nmk^2(n+m)+t)$,可以通过。

F 不会,我太菜了。

CF600(EDU2) 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-07-16 21:48:18

我 $6$ 个题 WA 了 $12$ 遍!

A Extract Numbers:

考察:字符串,模拟。
题目简述:
有一个字符串 $s$,里面有一些单词,它们两两之间用 ,; 分隔开,定义:

数字单词为只由阿拉伯数字组成的且不含前导 $0$ 的单词。 含前导 $0$ 指单词不为 $0$ 且以 $0$ 开头。

现在将所有数字单词放在一起,两两之间用 , 隔开,外面加上 " 并输出在第一行,将其它单词按同样方式处理输出在第二行。
对于任意一行,若没有单词满足条件,则在该行输出 -
数据范围:

  • $1\le |s|\le 10^5$

为方便处理边界,可在字符串前后都加上一个 ,
首先对于每个不在字符串开头的 ,;,记录它与字符串中上一个 , 之间的单词,根据题意判断它是否为数字单词,最后加到答案中即可。
时间复杂度为 $\Theta(n)$。

B Queries about less or equal elements:

考察:二分。
题目简述:
给你两个序列 $\{a_n\}$ 和 $\{b_m\}$,$\forall i\in[1,m]$,求:
$$\sum_{j=1}^n[a_j\le b_i]$$ 数据范围:

  • $1\le n,m\le 2\times 10^5$
  • $\forall i\in[1,n],j\in[1,m],-10^9\le a_i,b_j\le 10^9$

首先给 $\{a_n\}$ 排序,然后,$\forall i\in[1,m]$,注意到 $\{a_n\}$ 中第一个大于 $b_i$ 的数的下标即为 $\{a_n\}$ 中小于等于 $b_i$ 的数的个数加上 $1$,故使用 upper_bound 即可实现。

C Make Palindrome:

考察:字符串,贪心。
题目简述:
给你一个字符串 $s$,首先将它中的字母进行修改,然后将它重排,使它成为一个回文串,求当修改字母个数最少时,字典序最少的字符串。
数据范围:

  • $1\le |s|\le 2\times 10^5$

由于字符串可以任意重排,故在一开始就将字符串中的字符按 ASCII 码序升序排序。
我们要让修改字母数最少,那么我们先将相同的几对字符取一个放进新字符串中。
然后再将漏单的字符一个个地放进新字符串中,直到字符串的长度达到 $\displaystyle\lceil\frac{|s|}{2}\rceil$ 就终止,剩下的字符就是被修改的字符。
最后先正序再倒序输出这个新字符串,注意根据 $|s|$ 的奇偶性判断最后一个字符是输出一遍还是两遍。

D Area of Two Circles' Intersection:

考察:数学,计算几何。
题目简述:
给你两个圆,它们分别以点 $(x_1,y_1)$ 和 $(x_2,y_2)$ 为圆心,以 $r_1$ 和 $r_2$ 为半径,求这两个圆的面积交。
数据范围:

  • $-10^9\le x_1,y_1,x_2,y_2\le 10^9$
  • $1\le r_1,r_2\le 10^9$

两圆之间位置关系有 $3$ 种情况:

  1. 一圆完全包含另一圆,即 $\text{distance}((x_1,y_1),(x_2,y_2))\le|r_1-r_2|$,此时两圆面积交就为小圆面积,为 $\pi\min(r_1,r_2)^2$。
  2. 两圆没有交集,即 $\text{distance}((x_1,y_1),(x_2,y_2))\ge r_1+r_2$,此时两圆面积交为 $0$。
  3. 如图: 两圆面积交可表示为扇形 $ABD$ 和 $CBD$ 的面积和减去 $\Delta ABD$ 和 $\Delta CBD$ 的面积和。 那么现在当务之急是计算 $\ngle BAD$ 和 $\ngle BCD$,后面以计算 $\ngle BAD$ 为例。 注意到在 $\Delta ABC$ 中,三边已知,拿出我们的余弦定理: $$BC^2=AB^2+AC^2-2AB\cdot AC\cos\ngle BAC$$ 变形可得: $$\ngle BAC=\rccos(\frac{AB^2+AC^2-BC^2}{2AB\cdot AC})$$ 那么 $\ngle BAD=2\ngle BAC$。 求出该角后扇形和三角形的面积(分别设为 $S_1,S_2$)就很好求了: $$S_i=\frac{\ngle BAC\cdot r_1^2}{2},S_2=\frac{\sin\ngle BAC\cdot r_1^2}{2}$$

E Lomsat gelral:

考察:树上启发式合并。
题目简述:
给你一棵有 $n$ 个点的树,第 $i$ 个点上染了种类为 $c_i$ 的颜色。
若一个子树中颜色 $x$ 的出现次数非严格最多,则称其在该子树中占主导地位。
对于每个点,求以该点为根的子树的所有占主导地位的颜色种类的总和。
数据范围:

  • $1\le n\le 10^5$
  • $\forall i\in[1,n],1\le c_i\le n$

暴力求每个点的答案,开桶存储每种颜色的出现次数,但是注意在非叶子节点上直接由重儿子转移过来,保留原有数据,这样只有位于重链的点数据才会被清空,由于只有 $\Theta(\log n)$ 条重链,故总时间复杂度为 $\Theta(n\log n)$。

F Edge coloring of bipartite graph:

考察:二分图。
题目简述:
在一个左右各有 $a$ 个和 $b$ 个点的有 $m$ 条边的无重边的无向二分图上,对它的每条边染色,要求有公共顶点的两条边不能染成同一颜色,求最少染成几种不同的颜色,并给出一种方案。
数据范围:

  • $1\le a,b\le 10^5$
  • $0\le m\le 10^5$

观察样例得到一个可能的结论:答案就为每个点的最大度。
容易发现答案至少为每个点的最大度。
下面构造出答案为每个点的最大度的一种方案:
对于每条边,若两点上连的边所染颜色的 $\text{mex}$ 相同,则这条边就染这个颜色。
否则,我们还是染上两个 $\text{mex}$ 中的较小值,然后找出那条与其矛盾的边并给它染上较大值,若仍有矛盾则递归下去修改。
由于二分图没有奇环,同时偶环最后所得结论与一开始的 $\text{mex}$ 的前提条件,故不会产生环。
所以总时间复杂度为 $\Theta(m(a+b))$。

CF609(EDU3) 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-07-20 22:05:59

本场比赛没有计算几何题目,请放心食用。
但是我最擅长形式化(抽象化)题意了。

A. USB Flash Drives:

考察:排序,贪心。
题目简述:
求一个 $|S|$ 最小的满足下列条件的 $S$ 的大小 $|S|$。

  • $S\subseteq[1,n]\cap\mathbb Z$
  • $\displaystyle\sum_{i\in S}a_i\ge m$

其中整数 $n,m$ 及序列 $\{a_n\}$ 均已给出。
数据范围:

  • $1\le n\le 100$
  • $1\le m\le 10^5$
  • $\forall i\in[1,n],1\le a_i\le 1000$

显然,当 $i$ 对应的 $a_i$ 越大,$i$ 越要放进集合 $S$ 中,所以先将 $\{a_n\}$ 降序排序,然后边选取数边判断即可。
时间复杂度为 $\Theta(n\log n)$,空间复杂度为 $\Theta(n)$。

B. The Best Gift

考察:桶,模拟。
题目简述:
给你序列整数 $n,m$ 及序列 $\{a_n\}$,求:
$$\sum_{i=1}^n\sum_{j=i+1}^n[a_i\ne a_j]$$ 数据范围:

  • $2\le n\le 2\times 10^5$
  • $2\le m\le\color{red}10$
  • $\forall i\in[1,n]\cap\mathbb Z,1\le a_i\le m$

$m\le 10$ 的数据范围提醒了我们,注意到两个元素相同的不同位置的数本质上是相同的,然后我们把数放到桶 $\{t_m\}$ 里,对元素枚举,得到两种元素 $i,j$,使最终答案 $ans\leftarrow ans+t_it_j$ 即可。
时间复杂度为 $\Theta(n+m^2)$,空间复杂度为 $\Theta(m)$。

C. Load Balancing

考察:数学,贪心。
题目简述:
给定整数 $n$ 和序列 $\{m_n\}$,通过如下操作使得该序列极差最小,输出最小操作数。

选择两个数 $i,j$,满足 $i\ne j$ 且 $\{i,j\}\subseteq[1,n]\cap\mathbb Z$,使得 $m_i\leftarrow m_i-1$ 且 $m_j\leftarrow m_j+1$。

数据范围:

  • $1\le n\le 10^5$
  • $\forall i\in[1,n]\cap\mathbb Z,0\le m_i\le 2\times10^4$

首先,极差最小时一定为 $0$ 或 $1$。

极差肯定非负,若极差大于等于 $2$ 时不断将最大值减一最小值加一即可。

设 $\displaystyle sum=\sum_{i=1}^na_i$,则序列最后会有 $sum\bmod n$ 个数等于 $\displaystyle\lfloor\frac{sum}{n}\rfloor+1$,剩下的数都等于 $\displaystyle\lfloor\frac{sum}{n}\rfloor$,显然大的数最后变为较大的数一定不劣,故将序列排序后前 $sum\bmod n$ 个数变为 $\displaystyle\lfloor\frac{sum}{n}\rfloor+1$ 后面的同上,最后累加需加或减的次数,别忘除以 $2$。
时间复杂度为 $\Theta(n\log n)$,空间复杂度为 $\Theta(n)$。

D. Gadgets for dollars and pounds

考察:二分,贪心。
题意简述:
给你整数 $n,m,k,s$ 以及 $n\times 2$ 的矩阵 $x$,序列 $\{op_m\},\{a_m\}$。求满足以下条件的最小正整数 $p$ 并给出所有下文中 $\forall i\in[1,m]\cap\mathbb Z,S_i\ne 0$ 的所有 $i$ 和 $S_i$,若不存在,输出 $-1$。

  • 存在序列 $\{S_m\}$,满足:
    • $\forall i\in[1,m]\cap\mathbb Z,S_i\in[0,p]\cap\mathbb N$。
    • $\displaystyle\sum_{i=1}^m[S_i\ne 0]=k$。
    • $\displaystyle\sum_{i=1}^m[S_i\ne 0]\cdot a_ix_{S_i,op_i}\le s$。
  • $1\le p\le n$

数据范围:

  • $1\le n\le 2\times 10^5$
  • $1\le k\le m\le 2\times 10^5$
  • $1\le s\le 10^9$
  • $\forall i\in[1,n]\cap\mathbb Z,j\in\{1,2\},1\le x_{i,j}\le 10^6$
  • $\forall i\in[1,m]\cap\mathbb Z,op_i\in[1,2],1\le a_i\le 10^6$

首先 $p$ 具有单调性,我们可以对其二分。
然后 $\forall j\in\{1,2\}$,选择最小的 $x_{i,j}$ 是最好的,这样我们就可以预处理一个前缀最小值维护它,然后就分选或不选两种情况了。
我们可以先将 $\forall i\in[1,m]\cap\mathbb Z$ 的花费记录下来,即定义 $\displaystyle ans_i=a_i\min_{j=1}^px_{j,op_i}$,然后升序排序,模拟判断是否可行并记录方案即可。
时间复杂度为 $\Theta(m\log m\log n+n)$,空间复杂度为 $\Theta(n+m)$。

E. Minimum spanning tree for each edge

考察:生成树,倍增。
题目简述:
对于一个有 $n$ 个点 $m$ 条边的图 $G$,对于它的每条边求出包含这条边的最小生成树权值。
数据范围:

  • $1\le n\le 2\times 10^5$
  • $n-1\le m\le 2\times 10^5$

首先运用 Kruskal 算法求出该图的最小生成树,然后在最小生成树上运用类似倍增求 LCA 的方式,过程中再维护一个 $mx$ 数组来求出两点间的最大边,最后加上询问的这条边的边权减去求出的边权即可。
时间复杂度为 $\Theta(m\log m+m\log n+n\log n)$,空间复杂度为 $\Theta(n\log n+m)$。

F. Frogs and mosquitoes

考察:multiset。
题目简述:
这个抽象化题意太难描述了。
在一个数轴上,向右涂上了 $n$ 种颜色,第 $i$ 种颜色端点为 $x_i$,初始长度为 $t_i$,若一个点上有多种颜色,则以端点最靠左的颜色为准。
后面第 $i$ 次操作有一个【编不出来了】落在了 $p_i$ 处,不管这个【编不出来了】是否刚刚落下,只要它在一种颜色上,那么这种颜色的 $sum\leftarrow sum+1$ 且 $ans\leftarrow ans+b_i$。
最后输出每种颜色的 $sum$ 和 $ans$。
数据范围:

  • $1\le n,m\le 2\times 10^5$
  • $\forall i\in[1,n]\cap\mathbb Z,0\le x_i,t_I\le 10^9$
  • $\forall i\in[1,m]\cap\mathbb Z,0\le p_i,b_i\le 10^9$

首先,若 $x_i\le x_j$ 且 $x_i+t_i\ge x_j+t_j$,那么颜色 $j$ 就完全没有意义了,可以忽略,所以我们维护一个 update 函数来省去不必要的颜色。
然后我们把颜色和【编不出来了】都丢进 multiset 中按照题意维护即可。
时间复杂度为 $\Theta(n\log n+m\log m)$,空间复杂度为 $\Theta(n+m)$。

CF612(EDU4) 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-07-23 17:18:26

依然没有计算几何题。

A. The Text Splitting:

考察:字符串,模拟。
题目简述:
给你一个长度为 $n$ 的字符串 $s$ 以及两个正整数 $p,q$,现在让你把 $s$ 分割成若干个子串,使他们的长度为 $p$ 或 $q$。
最后以原来顺序输出子串个数及子串自身,多解任意输出,无解输出 -1
数据范围:

  • $1\le p,q\le n\le 100$

首先我们发现先分割长度为 $p$ 还是 $q$ 的字符串是无所谓的,所以我们可以默认先分割长度为 $p$ 的字符串,枚举子串个数,计算长度为 $q$ 的字符串个数,有解直接输出即可。
时间复杂度为 $\Theta(n)$,空间复杂度为 $\Theta(n)$。

B. HDD is Outdated Technology:

考察:模拟。
题目简述:
有一排列 $\{a_n\}$,定义从位置 $i$ 走到位置 $j$ 的代价为 $|i-j|$,求从排列上的值为 $1$ 的位置走到值为 $2$ 的位置,一直走到值为 $n$ 的位置的总代价。
数据范围:

  • $1\le n\le 2\times 10^5$

我们要快速找出值为 $i$ 的位置就要在读入后直接记录下来,然后直接模拟即可。
时间复杂度为 $\Theta(n)$,空间复杂度为 $\Theta(n)$。

C. Replace To Make Regular Bracket Sequence:

考察:栈,数学。
题目简述:
给你一个由 <>()[]{} 的字符串 $s$,定义 <([{ 为左括号,反之为右括号,每次操作可以将一个左括号改为其它左括号,也可以将一个右括号改为其它右括号。
求将其变为合法括号匹配的最小操作数,无解输出 Impossible
数据范围:

  • $1\le|s|\le 10^6$

我们可以先按普通括号匹配去做,先判断是否有解,然后统计匹配的不同左右括号对数,这就是答案。
时间复杂度为 $\Theta(n)$,空间复杂度为 $\Theta(n)$。

D. The Union of k-Segments:

考察:数学,差分,离散化。
题目简述:
在一条坐标轴上,给你 $n$ 条线段,再给你一个整数 $k$。如果一个点至少被 $k$ 条线段覆盖,那么这个点是符合条件的。
求在坐标轴上仅包含所有符合条件的线段的最小数量。
数据范围:

  • $1\le k\le n\le 10^6$

注意到一条线段就是在给一段区间区间加 $1$,最后就是在查询权值大于等于 $k$ 的点集合,这让我们想到差分。
查分前我们需要先将点离散化,但我们发现若相邻的两点分别为两条线段的一个端点且中间没被线段覆盖,但两条线段仍认为是一条线段。
那么我们想到我们在离散化时不只把线段端点离散化,还要和线段右端点加 $\displaystyle\frac{1}{2}$ 和左端点减 $\displaystyle\frac{1}{2}$ 一起离散化,都乘 $2$ 可以避免小数(最后别忘除回去就行)。
然后普通地进行差分即可,根据题意模拟输出。
时间复杂度为 $\Theta(n\log n)$,空间复杂度为 $\Theta(n)$。

E. Square Root of Permutation:

考察:图论。
题目简述:
给你一个排列 $\{p_n\}$,求一个排列 $\{q_n\}$,使得 $\forall i\in[1,n]\cap\mathbb Z,q_{q_i}=p_i$,若无解输出 -1
数据范围:

  • $1\le n\le 10^6$

如果我们给这个排列按照 $i\rightarrow p_i$ 的方式连边,发现形成了若干个环。
对于一个环($x_1\rightarrow x_2\rightarrow\cdots\rightarrow x_m\rightarrow x_1$),如果它是奇环,那么它会变成 $x_1\rightarrow x_3\rightarrow\cdots\rightarrow x_{m-1}\rightarrow x_2\rightarrow x_4\rightarrow\cdots\rightarrow x_m\rightarrow x_1$,如果它是偶环,它会变为两个环($x_1\rightarrow x_3\rightarrow\cdots\rightarrow x_{m-1}\rightarrow x_1$ 和 $x_2\rightarrow x_4\rightarrow\cdots\rightarrow x_m\rightarrow x_2$)。
由于排列 $\{p_n\}$ 中的偶环只可能由分割得到,故相等大小的偶环数量一定是偶数,这就是有解的判定。
然后根据上述还原回环即可。
时间复杂度为 $\Theta(n)$,空间复杂度为 $\Theta(n)$。

F. Simba on the Circle:

考察:dp,模拟,离散化。
题目简述:
在一个圆上顺时针标着 $1$ 到 $n$ 的 $n$ 个整数,第 $i$ 个数字大小为 $a_i$。
现在从位置 $s$ 顺时针或逆时针移动的同时选择数字,选择的数字产生的序列需要不降且所有数字都要被选择,设移动 $1$ 个单位长度花费 $1$ 的代价,选择数字不花费代价,求最小代价及方案。
数据范围:

  • $1\le s\le n\le 2000$
  • $\forall i\in[1,n]\cap\mathbb Z,-10^9\le a_i\le 10^9$

先设 dp 状态,设 $dp_i$ 为最后选择的数字是 $a_i$ 且当前在位置 $i$ 上的最小花费,$f_i$ 为所有 $a_i$ 已被选择且当前在位置 $i$ 上的最小花费。
下面分两个步骤:

  1. 由 $f_{i-1}$ 转移到 $dp_i$:
    这个很简单,在预处理时将所有 $a_i$ 离散化,将相同的 $a_i$ 封装到一个名叫 $idx_{a_i}$ 的 vector 里,然后枚举 $idx_{i-1}$ 和 $idx_i$ 中的数,直接转移即可。
  2. 由 $dp_i$ 转移到 $f_i$:
    很显然,在 $a_x=a_y$ 的情况下从 $x$ 绕整个圆一圈回到 $x$ 旁边的 $y$ 是最优的,那么直接枚举 $x$ 以及 $y$ 是在 $x$ 的顺时针还是逆时针方向即可。

这样就可以求出最小代价,方案在 dp 过程中记录即可。
时间复杂度为 $\Theta(n^2)$,空间复杂度为 $\Theta(n)$。

CF616(EDU5) 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2025-07-25 21:59:53

开始将同学对号入座进题面了。

A. Comparing Two Long Integers

考察:高精度。
题目简述:
小 I 收到了两个很大的非负整数 $a,b$,由于他很糖(指高血糖,不信你看他名字)他需要判断 $a$ 和 $b$ 的大小关系。$a$ 和 $b$ 可以有前导 $0$。
数据范围:

  • 令 $|a|,|b|$ 分别代表 $a,b$ 的位数,则 $1\le|a|,|b|\le 10^6$

在小 I 的小学时学过,比较两个数大小一共分两步:

  • 比较两个数的位数。
  • 从高位开始逐位比较。

但他发现这一切都建立在两个数不含前导零之上,所以若包含前导零他可以先将数位少的数用前导 $0$ 补满数位,使他们位数相同,再直接进行第二步即可。
时间复杂度为 $\Theta(|a|+|b|)$,空间复杂度为 $\Theta(|a|+|b|)$。

B. Dinner with Emma

考察:模拟,贪心。
题目简述:
给你一个 $n\times m$ 的矩阵 $c$,现在现由小 X 选择一个 $i\in[1,n]\cap\mathbb Z$,再由小 K 选择一个 $j\in[1,m]\cap\mathbb Z$,得到一个数 $c_{i,j}$,小 X 想让这个数更大,小 K 想让这个数更小,为了防止初一生吵架,你需要给出最后会得到的 $c_{i,j}$ 值为多少。
数据范围:

  • $1\le n,m\le 100$
  • $\forall i\in[1,n]\cap\mathbb Z,j\in[1,m]\cap\mathbb Z,1\le c_{i,j}\le 10^9$

如果小 X 选好了 $i$,那么小 K 一定会选择最小的一个,即 $\displaystyle\min_{j=1}^mc_{i,j}$。
那么小 X 选的时候肯定想最后值最大,所以最后答案为 $\displaystyle\max_{i=1}^n\min_{j=1}^mc_{i,j}$。
时间复杂度为 $\Theta(nm)$,空间复杂度可为 $\Theta(1)$。

C. The Labyrinth

考察:dfs。
题目简述:
小 A 收到了一个 $n\times m$ 的网格图,它由 .* 组成,* 指有人阻挡他,. 反之。. 组成的四连通块是小 A 的活动区域,但是小 A 有一个足球,它可以击中一个人的脸并将相应位置的 * 变为 .,现在被击中的人相应位置的四连通块就变成了自己的活动区域,这样可以扩大自己的活动区域。
小 A 还不确定要击中谁,所以希望你求出击中任意一个人后活动区域的大小,同时为了排版整齐,他会让你给出一个表格,若该格本来就为 . 则还输出 .,反之输出该格答案对 $10$ 取模的值。
数据范围:

  • $1\le n,m\le 1000$

很明显,我们可以先跑一遍 dfs,求出每个网格所属连通块以及每个连通块的大小,然后对于每个 *,找到它的四联通格,对他们所属的连通块进行去重,累加答案即可。
时间复杂度为 $\Theta(nm)$,空间复杂度为 $\Theta(nm)$。

D. Longest k-Good Segment

考察:双指针,桶。
题目简述:
小 Z 收到了一个序列 $\{a_n\}$ 和一个整数 $k$,喜欢玩 Genshin 牌的他一下就来了兴趣,想要知道如果一个连续子序列的整数种类不超过 $k$ 种,那么当连续子序列长度最大时,这个连续子序列的左右端点可以是多少(给出一种即可)。
数据范围:

  • $1\le k\le n\le 5\times 10^5$
  • $\forall i\in[1,n]\cap\mathbb Z,0\le a_i\le 10^6$

为了统计序列整数种类,小 Z 开了一堆桶,实时存储整数个数,当被清零时整数种类减 $1$,从零变到一时种类加 $1$。
然后带进双指针板子即可。
时间复杂度为 $\Theta(n)$,空间复杂度为 $\Theta(n+V)$。

E. Sum of Remainders

考察:整除分块。
题目简述:
小 L 收到了两个整数 $n,m$,比小 Z 还爱玩原神的他,想求:
$$(\sum_{i=1}^mn\bmod i)\bmod(10^9+7)$$ 数据范围:

  • $1\le n,m\le 10^{13}$

整除分块板子题,给原式变个形:
$$\begin{aligned}(\sum_{i=1}^mn\bmod i)\bmod(10^9+7)&=(\sum_{i=1}^mn-i\lfloor\frac{n}{i}\rfloor)\bmod(10^9+7)\&=(nm-\sum_{i=1}^mi\lfloor\frac{n}{i}\rfloor)\bmod(10^9+7)\end{aligned}$$ 后半部分上整除分块就行了。
时间复杂度为 $\Theta(\sqrt m)$,空间复杂度为 $\Theta(1)$。

[AGC020D] Min Max Repetition 讲解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-12 13:08:06

[AGC020D] Min Max Repetition

题目考察:贪心,数学,二分。
题目简述:
$q$ 次询问,给你 $a,b,c,d$,有一字符串 $s$:

  • $\displaystyle|s|=a+b$,$s$ 由 $a$ 个 A 和 $b$ 个 B 组成。
  • 同时,最大连续相同字符数最小。
  • 同时,字典序最小。

求该字符串的第 $c$ 位到第 $d$ 位。
数据范围:

  • $1\le q\le 10^3$
  • $1\le a,b\le 5\times 10^8$
  • $1\le c\le d\le a+b$
  • $1\le d-c+1\le 100$

首先我们肯定可以得出最小的最大连续相同字符数是 $\displaystyle k=\max(\lceil\frac{a}{b+1}\rceil,\lceil\frac{b}{a+1}\rceil)$。
我们让 B 尽可能靠后,那么这个字符串长成这样: $$\text{AAA\dots ABAAA\dots ABAAA\dots ABAAA\dots ABB\dots BABB\dots BABB\dots BB}$$ 那么我们可以二分找出其分界点,那么我们还需要一个判断条件。
设分界点为 $x$,则 A 在前面使用了 $\displaystyle\lfloor\frac{x}{k+1}\rfloor\times k+x\bmod (k+1)$ 个,那么 A 在后面就要使用 $\displaystyle a-\lfloor\frac{x}{k+1}\rfloor\times k-x\bmod (k+1)$ 个;B 在前面使用了 $\displaystyle\lfloor\frac{x}{k+1}\rfloor$ 个,那么 B 在后面使用了 $\displaystyle b-\lfloor\frac{x}{k+1}\rfloor$ 个。
则我们得到判断条件: $$(a-\lfloor\frac{x}{k+1}\rfloor\times k-x\bmod (k+1))\times k<b-\lfloor\frac{x}{k+1}\rfloor$$ 然后我们发现在前半部分当 $i\bmod (k+1)=0$ 时 $s_i$ 是 BA 反之;在后半部分当 $(a+b-k+1)\bmod (k+1)=0$ 时是 BA 反之。

单次询问时间复杂度为 $\Theta(\log_2(a+b)+(d-c))$。
代码:

#include<bits\/stdc++.h>
#define inl inline
#define reg register
#define int long long
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define rep(i,x,y) for(reg int i=x;i<=(y);++i) 
#define per(i,x,y) for(reg int i=x;i>=(y);--i)
#define rpr(i,x,y,z) for(reg int i=x;i<=(y);i+=z)
#define epe(i,x,y,z) for(reg int i=x;i>=(y);i-=z)
#define endl '\n'
#define INF (int)1e16
#define pb push_back
#define fi first
#define se second
#define lcm(x,y) x\/__gcd(x,y)*y
#define ull unsigned long long
#define prr make_pair
#define pii pair<int,int> 
#define gt(s) getline(cin,s)
#define at(x,y) for(reg auto x:y)
#define ff fflush(stdout)
#define mt(x,y) memset(x,y,sizeof(x))
#define idg isdigit
#define fp(s) string ssss=s;freopen((ssss+".in").c_str(),"r",stdin);freopen((ssss+".out").c_str(),"w",stdout);
#define sstr stringstream 
using namespace std;
inl bool check(int x,int k,int a,int b){
\/\/	cout<<(a-x\/(k+1)*k-x%(k+1))<<' '<<b-x\/(k+1)<<endl;
	return (a-x\/(k+1)*k-x%(k+1))*k<b-x\/(k+1);
}
inl int lower(int l,int r,int k,int a,int b){
	reg int mid=0,ans=-1,fl=l,fr=r;
	while(l<=r){
		mid=l+r>>1;
\/\/		cout<<mid<<endl;
		if(check(mid,k,a,b)){
			r=mid-1;
			ans=mid;
		}else l=mid+1;
	}
	if(ans!=-1) return ans;
	else if(fl==l) return -1;
	else return r+1;
}
inl void solve(){
	reg int a,b,c,d,k,x;
	cin>>a>>b>>c>>d;
	k=max(max((int)ceil(a\/(b+1.0)),(int)ceil(b\/(a+1.0))),1ll);
	x=lower(0,a+b,k,a,b);
\/\/	cout<<k<<' '<<x<<endl;
	rep(i,c,d)
		if(i<=x)
			if(i%(k+1)) cout<<'A';
			else cout<<'B';
		else if((a+b-i+1)%(k+1)) cout<<'B';
		else cout<<'A';
	cout<<endl;
}
signed main(){
	fst;
	reg int t;
	cin>>t;
	while(t--) solve();
	return 0;
}

【2024暑假】-普及6 讲解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-13 13:52:38

P8649 [蓝桥杯 2017 省 B] k 倍区间

P8649 [蓝桥杯 2017 省 B] k 倍区间

题目考察:前缀和,桶。
题目简述:
给你一个序列 $\{a_n\}$,求:
$$\sum_{i=1}^n\sum_{j=i}^n[(\sum_{x=i}^ja_x)\bmod k=0]$$ 数据范围:

  • $1\le n,k,a_i\le 10^5$

设 $sum_i=\sum_{j=1}^ia_j$,由于 $(sum_r-sum_{l-1})\bmod k=0$,所以 $sum_r\equiv sum_{l-1}\pmod k$,那么我们开一个桶,每次将 $t_{sum_i\bmod k}$ 加 $1$,累加答案即可。

时间复杂度为 $\Theta(n)$。
代码:

#include<bits\/stdc++.h>
#define int long long
#define endl '\n'
#define inl inline
#define reg register
#define rep(i,x,y) for(reg int i=x;i<=y;++i)
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;
const int N=1e5+5;
int n,k,a[N],sum[N],mp[N],ans;
signed main(){
	fst;
	cin>>n>>k;
	rep(i,1,n) cin>>a[i];
	rep(i,1,n) sum[i]=(sum[i-1]+a[i])%k;
	++mp[0];
	rep(i,1,n) ans+=mp[sum[i]]++;
	cout<<ans;
	return 0;
} 

P8247 皇室战争

P8247 皇室战争

题目考察:斜率。
题目简述:
有一位战士,他有一些箭,他要射死一些敌人。他的箭能穿透,求他最少要射几次。
数据范围:

  • $1\le n\times m\le 10^6$

我们要求战士到敌人的斜率,斜率相同的就是能一箭射死的。
由于斜率要求浮点数,浮点数的精度可能会出问题,我们需要像一种其他的方法。
如果 $\displaystyle\frac{a}{b}=\frac{c}{d}$,则 $a\div \gcd(a,b)=c\div\gcd(c,d),b\div\gcd(a,b)=d\div\gcd(c,d)$,那么我们将其除以 $|\gcd(a,b)|$ 即可。

时间复杂度为 $\Theta(nm\log_2(nm))$。
代码:

#include<bits\/stdc++.h>
#define int long long
#define endl '\n'
#define inl inline
#define reg register
#define rep(i,x,y) for(reg int i=x;i<=y;++i)
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define prr make_pair
using namespace std;
const int N=1e6+5;
int n,m,sx,sy,k;
vector<char>g[N];
char ch;
map<pii,bool>mp;
signed main(){
	fst;
	cin>>n>>m;
	rep(i,1,n) g[i].pb(' ');
	rep(i,1,n)
		rep(j,1,m){
			cin>>ch;
			g[i].pb(ch);
		}
	rep(i,1,n)
		rep(j,1,m)
			if(g[i][j]=='S'){
				sx=i;
				sy=j;
			}
	rep(i,1,n)
		rep(j,1,m)
			if(g[i][j]=='K'){
				k=abs(__gcd(i-sx,j-sy));
				mp[prr((i-sx)\/k,(j-sy)\/k)]=1;
			}
	cout<<mp.size();
	return 0;
} 

P1131 [ZJOI2007] 时态同步

P1131 [ZJOI2007] 时态同步

题目考察:树,dp,dfs。
题目简述:
给你一颗以 $s$ 为根的树,有一种操作:

  • 选择一条边,将其边权增加 $1$。

求使 $s$ 节点到所有叶子节点的距离都相等的操作数。
数据范围:

  • $1\le n\le 5\times 10^5$

首先我们肯定可以通过一遍 dfs 得出以任意一点为根的子树的最大的 $s$ 节点到子树内叶子节点的距离($mx_u$),那么如果 $mx_u<mx_s$,说明在这个点和其父节点的这条边上操作 $mx_s-mx_u$ 次是最合适的,这样我们再来一遍 dfs 就可以了。

时间复杂度为 $\Theta(n)$。
代码:

#include<bits\/stdc++.h>
#define int long long
#define endl '\n'
#define inl inline
#define reg register
#define rep(i,x,y) for(reg int i=x;i<=y;++i)
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define prr make_pair
#define at(v,x) for(reg auto v:x)
using namespace std;
const int N=5e5+5;
struct node{
	int v,w;
	inl node(){
		v=w=0;
	}
	inl node(int v,int w):v(v),w(w){}
};
vector<node>g[N];
int n,s,u,v,w,mx[N],c[N],ans;
bool vis[N];
inl void dfs(int u,int t){
	vis[u]=1;
	mx[u]=t;
	at(x,g[u])
		if(!vis[x.v]){
			dfs(x.v,t+x.w);
			mx[u]=max(mx[u],mx[x.v]);
		}
	vis[u]=0;
}
inl void dfs2(int u){
	vis[u]=1;
	at(x,g[u])
		if(!vis[x.v]){
			mx[x.v]+=c[u];
			c[x.v]+=c[u];
		}
	c[u]=0;
	at(x,g[u])
		if(!vis[x.v]){
			c[x.v]+=mx[s]-mx[x.v];
			ans+=mx[s]-mx[x.v];
			mx[x.v]=mx[s];
			dfs2(x.v);
		}
	vis[u]=0;
}
signed main(){
	fst;
	cin>>n>>s;
	rep(i,2,n){
		cin>>u>>v>>w;
		g[u].pb(node(v,w));
		g[v].pb(node(u,w));
	}
	dfs(s,0);
	dfs2(s);
	cout<<ans;
	return 0;
} 

P8270 [USACO22OPEN] Subset Equality S

P8270 [USACO22OPEN] Subset Equality S

题目考察:dp,状态压缩,字符串。
题目简述:
给你两个字符串 $s,t$,$q$ 次询问,每次给定一个字符串 $op$,求若 $s$ 和 $t$ 只包含 $op$ 里面的字符,$s$ 和 $t$ 是否相等。
数据范围:

  • $1\le |s|,|t|,q\le 10^5$
  • $1\le |op|\le r$($r=18$)
  • $\forall i\in[1,|s|],\forall j\in[1,|t|],\forall k\in[1,|op|],s_i,t_j,op_k$ 都是 ar 的字符。

这个题有一个结论:若 $|op|\ge 3$ 且他的任意子序列的方案是合法的,则他就是合法的。
下面我们来证明:
两个字符串:
$$\text{abc},\text{acb}$$ 则 $\text{bc},\text{cb}$ 方案不是合法的。
所以我们暴力跑出 $|op|\le 2$ 的合法性,然后进行状压 dp 即可。

时间复杂度为 $\Theta(r^2(n+m)+2^rr+q)$。
代码:

#include<bits\/stdc++.h>
#define int long long
#define endl '\n'
#define inl inline
#define reg register
#define rep(i,x,y) for(reg int i=x;i<=y;++i)
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define prr make_pair
#define at(v,x) for(reg auto v:x)
using namespace std;
const int N=25,M=(1<<18)+5;
string s,t,tmps,tmpt,op;
int q,n,m,sum[N],tmp;
bool dp[M];
signed main(){
	fst;
	cin>>s>>t>>q;
	n=s.size();
	m=t.size();
	s=' '+s;
	t=' '+t;
	rep(i,1,n) ++sum[s[i]-'a'+1];
	rep(i,1,m) --sum[t[i]-'a'+1];
	rep(i,1,18) dp[1ll<<i-1]=!sum[i];
	rep(i,1,18)
		rep(j,i+1,18){
			tmps=tmpt="";
			rep(k,1,n)
				if(s[k]==i+'a'-1||s[k]==j+'a'-1) tmps+=s[k];
			rep(k,1,m)
				if(t[k]==i+'a'-1||t[k]==j+'a'-1) tmpt+=t[k];
			dp[1ll<<i-1|1ll<<j-1]=tmps==tmpt;
		}
	rep(i,0,(1ll<<18)-1)
		if(__builtin_popcount(i)>=3){
			dp[i]=1;
			rep(j,1,18)
				if(i>>j-1&1) dp[i]&=dp[i&~(1ll<<j-1)];
		}
	rep(i,1,q){
		tmp=0;
		cin>>op;
		at(ch,op) tmp|=1ll<<ch-'a';
		if(dp[tmp]) cout<<"Y";
		else cout<<"N";
	}
	return 0;
} 

P6280 [USACO20OPEN] Exercise G 讲解

P6280 [USACO20OPEN] Exercise G

题目考察:数学,数论,素数筛,dp。
题目简述:
给你一个 $n$,求满足下列条件的数 $k$ 的和对 $m$ 取模的结果:

  • 有一个排列 $\{a_n\}$,$k$ 个新排列 $\{b_n\}$,$b_{0,i}=i$,使 $b_{l,a_i}=b_{l-1,i}$,最后使 $\forall i\in[1,n],b_{0,i}=b_{k,i}$。

数据范围:

  • $1\le n\le 10^4$
  • $1\le m\le 10^9+7$
  • $m\in\text{prime}$

假设相邻两个排列之间有一些置换群,例如 $\{1,2,3,4,5\}$ 和 $\{2,3,1,5,4\}$,$1,2,3$ 和 $4,5$ 就是置换群,则设置换群的大小为 $siz_i$,那么会进行 $\text{lcm}_{i=1}^{size}siz_i$ 轮,则:

  • $\displaystyle\sum_{i=1}^{size}siz_i\le n$
  • $\text{lcm}_{i=1}^{size}siz_i=k$

由于 $\text{lcm}$ 只与质数及其幂次有关,所以我们先将这 $cnt$ 个质数筛出来($cnt\pprox \frac{n}{\log_2n}$)。
现在我们就可以进行 dp,设 $dp_{i,j}$ 为进行到第 $i$ 个质数,和已有了 $j$ 的和,易得状态转移方程:
$$dp_{i,j}=\sum_{k=1}^{\lfloor\log_{prime_i}n\rfloor}dp_{i-1,j-prime_i^k}\times prime_i^k$$ 同样的我们可以压掉第一维。

时间复杂度为 $\Theta(n^2)$。
代码:

#include<bits\/stdc++.h>
#define int long long
#define endl '\n'
#define inl inline
#define reg register
#define rep(i,x,y) for(reg int i=x;i<=y;++i)
#define per(i,x,y) for(reg int i=x;i>=y;--i)
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define prr make_pair
#define at(v,x) for(reg auto v:x)
using namespace std;
const int N=1e4+5;
int n,m,cnt,prime[N],f[N],ans,tmp;
bool isprime[N];
inl void init(int x){
	rep(i,2,x)
		if(!isprime[i]){
			prime[++cnt]=i;
			rep(j,2,x\/i) isprime[i*j]=1;
		}
}
signed main(){
	fst;
	cin>>n>>m;
	init(n);
	f[0]=1;
	rep(i,1,cnt)
		per(j,n,prime[i]){
			tmp=prime[i];
			while(tmp<=j){
				(f[j]+=f[j-tmp]*tmp%m)%=m;
				tmp*=prime[i];
			}
		}
	rep(j,0,n) (ans+=f[j])%=m;
	cout<<ans;
	return 0;
} 

[ABC362F] Perfect Matching on a Tree 讲解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-15 22:20:05

[ABC362F] Perfect Matching on a Tree

题目考察:dfs,重心。
题目简述:
给你一棵 $n$ 个点的树,选出 $\displaystyle\lfloor\frac{n}{2}\rfloor\times2$ 个点,这些点两两不同,有 $\displaystyle\lfloor\frac{n}{2}\rfloor$ 个点设为 $\{x_{\lfloor\frac{n}{2}\rfloor}\}$,另外 $\displaystyle\lfloor\frac{n}{2}\rfloor$ 个点设为 $\{y_{\lfloor\frac{n}{2}\rfloor}\}$,使得 $\displaystyle\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}\text{dist}(x_i,y_i)$ 最大。
其中 $\text{dist}(x,y)$ 表示从点 $x$ 到点 $y$ 所经过的最少边数。
数据范围:

  • $2\le n\le 2\times 10^5$

首先我们猜想,若 $\displaystyle\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}\text{dist}(x_i,y_i)$ 最大,则从 $x_i$ 到 $y_i$ 的这条路径一定经过了这棵树的重心。
证明:
我们设根节点为重心,再设从 $x_k$ 到 $y_k$ 的这条路径没有经过这棵树的重心,则一定有一从 $x_j$ 到 $y_j$ 的路径不与其相交,因为若所有 $x_i$ 到 $y_i$ 的路径都与其相交,则所有的点对中要么有至少一个点在 $x_k$ 和 $y_k$ 的子树内(然而这是不可能的,因为根据重心的定义,不会有一个子树的大小超过 $\displaystyle\lfloor\frac{n}{2}\rfloor$),要么所有的点都能在不经过重心的情况下到达 $x_k$ 到 $y_k$ 的路径上的一个点(然而这也是不可能的,因为这个点、$x_k$ 和 $y_k$ 路径上的一个点和重心形成了一个环,这不符合树的定义)。总而言之,所有 $x_i$ 到 $y_i$ 的路径两两相交。
下面我们继续来证明选重心为根节点存在最优解。若不选重心作伪根节点,则有一组 $x_k$ 和 $y_k$ 没有经过重心,那么我们找来一组 $x_j$ 和 $y_j$($x_j$ 和 $y_j$ 都不在 $x_k$ 和 $y_k$ 所在的子树内),将 $y_j$ 与 $y_k$ 交换,若存在最优解,则 $\text{dist}(x_j,y_j)+\text{dist}(x_k,y_k)<\text{dist}(x_j,y_k)+\text{dist}(x_k,y_j)$。
下面我们来证明该式成立(下面设点 $u$ 的深度为 $dep_u$,$u$ 和 $v$ 的 LCA 为 $\text{lca}(u,v)$):
$$\text{dist}(x_j,y_j)+\text{dist}(x_k,y_k)=dep_{x_j}+dep_{y_j}+dep_{x_k}+dep_{y_k}-2dep_{\text{lca}(x_k,y_k)}$$ 而:
$$\text{dist}(x_j,y_k)+\text{dist}(x_k,y_j)=dep_{x_j}+dep_{y_j}+dep_{x_k}+dep_{y_k}$$ 因为 $dep_{\text{lca}(x_k,y_k)}>0$,所以 $\text{dist}(x_j,y_j)+\text{dist}(x_k,y_k)<\text{dist}(x_j,y_k)+\text{dist}(x_k,y_j)$。
证毕。

由于选择重心的话最后的答案是相同的(甚至选一个最大子树的大小不大于 $\displaystyle\lfloor\frac{n}{2}\rfloor$ 都可以),上面已经证明,答案是 $\displaystyle\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}dep_{x_i}+dep_{y_i}$,则我们在其里面任选一个即可。

那么我们考虑如何选出这些点对,由于重心的基本性质没有任意一颗子树的大小超过 $\displaystyle\lfloor\frac{n}{2}\rfloor$,所以我们设 $\{dfn_n\}$ 为以树的重心为根所得的前序遍历,则点 $dfn_i$ 和点 $\displaystyle dfn_{i+\lfloor\frac{n}{2}\rfloor}$ 一定不在一个子树内,所以将 $dfn_i$ 和 $\displaystyle dfn_{i+\lfloor\frac{n}{2}\rfloor}$ 放在一组是没有问题的。
如果 $n$ 是奇数怎么办?由于答案是 $\displaystyle\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}dep_{x_i}+dep_{y_i}$,所以我们要丢弃最小的 $dep_i$,于是我们丢弃根节点(重心)。

时间复杂度为 $\Theta(n)$。
代码:

#include<bits\/stdc++.h>
#define inl inline
#define reg register
#define int long long
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define rep(i,x,y) for(reg int i=x;i<=(y);++i) 
#define per(i,x,y) for(reg int i=x;i>=(y);--i)
#define rpr(i,x,y,z) for(reg int i=x;i<=(y);i+=z)
#define epe(i,x,y,z) for(reg int i=x;i>=(y);i-=z)
#define repe(i,x,y) for(i=x;i<=(y);++i) 
#define endl '\n'
#define INF 1e16
#define pb push_back
#define fi first
#define se second
#define lcm(x,y) x\/__gcd(x,y)*y
#define ull unsigned long long
#define prr make_pair
#define pii pair<int,int> 
#define gt(s) getline(cin,s)
#define at(x,y) for(reg auto x:y)
#define ff fflush(stdout)
#define mt(x,y) memset(x,y,sizeof(x))
#define idg isdigit
#define fp(s) string ssss=s;freopen((ssss+".in").c_str(),"r",stdin);freopen((ssss+".out").c_str(),"w",stdout);
#define sstr stringstream 
#define all(x) x.begin(),x.end()
#define mcy(a,b) memcpy(a,b,sizeof(b))
using namespace std;
const int N=2e5+5;
vector<int>g[N],dfn(1,0);
bool vis[N];
int siz[N],mx[N];
inl void dfs(int u,int &rt,int n){
	vis[u]=siz[u]=1;
	at(v,g[u])
		if(!vis[v]){
			dfs(v,rt,n);
			siz[u]+=siz[v];
			mx[u]=max(mx[u],siz[v]);
		}
	mx[u]=max(mx[u],n-siz[u]);
	if(mx[u]<=n>>1) rt=u;
	vis[u]=0;
}
inl void dfs2(int u){
	vis[u]=1;
	dfn.pb(u);
	at(v,g[u])
		if(!vis[v]) dfs2(v);
	vis[u]=0;
}
signed main(){
	fst;
	reg int n,rt=0;
	cin>>n;
	rep(i,2,n){
		reg int u,v;
		cin>>u>>v;
		g[u].pb(v);
		g[v].pb(u);
	}
	dfs(1,rt,n);
	dfs2(rt);
	if(n&1)
		rep(i,2,(n>>1)+1) cout<<dfn[i]<<' '<<dfn[i+(n>>1)]<<endl;
	else rep(i,1,n>>1) cout<<dfn[i]<<' '<<dfn[i+(n>>1)]<<endl;
	return 0;
}

[ABC363F] Palindromic Expression 讲解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-22 17:59:47

[ABC363F] Palindromic Expression

题目考察:记忆化,搜索,数学。
题目简述:
给你一个数 $n$,求一个满足以下条件的字符串 $s$,若没有一个字符串满足条件,输出 $-1$:

  • $s$ 由 123456789*(即为乘号),注意没有 0
  • $s$ 是一个回文串。
  • $s$ 是一个合法的算术表达式且最后计算结果等于 $n$。

数据范围:

  • $1\le n\le 10^{12}$

考虑暴力搜索。
对于每一个 $n$,我们考虑找到一对 $n$ 的回文因数 $a,b$(在这里定义回文因数为:若 $a,b$ 为 $n$ 的因数,且 $a,b$ 互为回文,则称 $a,b$ 是一对回文因数),同时 $a,b$ 还不能包含 $0$,那么我们将 $n$ 除以 $a$ 和 $b$,继续往下搜索。
但这样还不行,因为对于 $n=48$,可以经过 2*2*3*2*2 搜索到 $3$,也可以经过 4*3*4 搜索到 $3$,所以我们要用一个 map 来记忆化,也可以用 unordered_map 来省去一个 $\log$。

最终时间复杂度为 $\Theta(\sqrt n\log n)$。
代码:

#include<bits\/stdc++.h>
#define inl inline
#define reg register
#define int long long
#define fst ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define rep(i,x,y) for(reg int i=x;i<=(y);++i) 
#define per(i,x,y) for(reg int i=x;i>=(y);--i)
#define rpr(i,x,y,z) for(reg int i=x;i<=(y);i+=z)
#define epe(i,x,y,z) for(reg int i=x;i>=(y);i-=z)
#define repe(i,x,y) for(i=x;i<=(y);++i) 
#define endl '\n'
#define INF 1e16
#define pb push_back
#define fi first
#define se second
#define lcm(x,y) x\/__gcd(x,y)*y
#define ull unsigned long long
#define prr make_pair
#define pii pair<int,int> 
#define gt(s) getline(cin,s)
#define at(x,y) for(reg auto x:y)
#define ff fflush(stdout)
#define mt(x,y) memset(x,y,sizeof(x))
#define idg isdigit
#define fp(s) string ssss=s;freopen((ssss+".in").c_str(),"r",stdin);freopen((ssss+".out").c_str(),"w",stdout);
#define sstr stringstream 
#define all(x) x.begin(),x.end()
#define mcy(a,b) memcpy(a,b,sizeof(b))
using namespace std;
map<int,string>mp;
inl int rvs(int n){
	reg int x=n,y=0;
	while(x){
		y=(y<<1)+(y<<3)+x%10;
		x\/=10;
	}
	return y;
}
inl bool check0(int n){
	reg int x=n;
	while(x){
		if(!(x%10)) return 0;
		x\/=10;
	}
	return 1;
}
inl bool check(int n){
	return n==rvs(n);
}
inl string getans(int n){
	if(mp.count(n)) return mp[n];
	if(check(n)&&check0(n)) return mp[n]=to_string(n);
	rep(i,2,sqrt(n)){
		reg int rvsi=rvs(i);
		if(n%(i*rvsi)||!check0(i)||!check0(rvsi)) continue;
		reg string s=getans(n\/i\/rvsi);
		if(s=="-1") continue;
		return mp[n]=to_string(i)+"*"+s+"*"+to_string(rvsi);
	}
	return "-1";
}
signed main(){
	fst;
	reg int n;
	cin>>n;
	cout<<getans(n);
	return 0;
}
共 127 篇博客