AtCoder Beginner Contest 254

Ex - Multiply or Divide by 2

题目大意,给定两个集合 AABB,每一次你可以把 AA 中的数 xx 替换成 2x2x
或者把 xxx2\displaystyle \left\lfloor \frac{x}{2} \right\rfloor 替换
问最少几次可以让 A,BA, B 两个集合相同(计算重复元素)

方法一,维护堆
对于 AA 中的元素 xxBB 中的元素 yy,以上操作影响的是最后一位

  • 首先考虑到如果 yy 最后一位是 11,但 xx 最后一位是 00,那么无解
    因为两种操作都不能够在某一位上引入一个 11
  • 考虑 yy 的最后一位是 00,那么如果 x>yx > y,那么只要令 x1x \gg 1
    然后比较 xx 更高的位,而如果 x<yx < y 呢?
  • 考虑 x<yx < y,假设当前处理的是 xxii 位与 yyjj
    此时令 x1x \ll 1,这样 x[i+1]x[i+1]y[j]y[j] 都是 00
    然后接着比较 x[i]x[i]y[j1]y[j-1],所以这一步的操作,等价于 y1y \gg 1
  • 上述是一个 max>max\max > \max 的问题,可以考虑用两个堆维护
    如果 Amax>BmaxA_{\max} > B_{\max},尝试令最大值 x1x \gg 1,然后继续比较
    反之则需要判断 BB 最大值 xx 的最后一位是不是 00
  • 最后只有在堆为空的时候,算法执行成功

方法二,01-Trie
上述过程还有一种模拟方法,就是用 01-trie 来维护

  • 先把所有的数看成 01 串,插入 trie 中,然后 dfs
  • 从叶节点自底向上统计,因为我们需要让 AA 中的元素 B\to B
    所以插入 trie 的时候,对于 AA 集合元素,当它沿着树边走的时候,让沿途节点权值 +1+1
    同理,插入 BB 集合,让沿途节点权值 1-1,这样如果 trie 中的节点权值为 00
    就说明这个位置,A,BA, B 两个集合匹配上了
  • 自底向上修改,如果当前字符表示 00,那么根据方法一的分析
    如果点的权值 v>0v > 0,说明集合 AA 中有一些元素比集合 BB 中大
    对这些元素每一个都执行 1\gg 1ans+=vans += v
    同理如果 v<0v < 0ans+=vans += |v|
  • 然后考虑当前字符表示 11,如果 v<0v < 0,说明 BB 中以 11 结尾的元素个数大于 AA 中相应的个数
    而这些 11 并不能通过规定的操作删去,所以直接返回错误
    否则的话,操作次数为 ans+=vans += v
  • 然后回溯到根,就做完了

Rectangle GCD
题目大意,给你两个数组 A[1n],B[1n]A[1\cdots n], B[1\cdots n],构成 n×nn \times n 的矩阵
矩阵中 (i,j)(i, j) 的值定义为 Ai+BjA_i + B_j
QQ 个询问,每次询问给出 [h1,h2],[w1,w2][h_1, h_2], [w_1, w_2],问 [h1,h2]×[w1,w2][h1, h2] \times [w_1, w_2] 区域内
所有数的最大公约数

算法分析
对于 GCD\text{GCD},有一个很重要的结论
gcd(x0,x1,x2,,xn)=gcd(x0,x1x0,x2x1,,xixi1,)\text{gcd}(x_0, x_1, x_2, \cdots, x_n) = \text{gcd}(x_0, x_1-x_0, x_2 -x_1, \cdots, x_i - x_{i-1}, \cdots)

矩阵的 gcd\text{gcd} 可以写成如下形式,对列作差分

(Ai+BjAi+Bj+1Ai+Bj+2Ai+BjAi+1AiAi+2Ai+1AiAi1)\begin{pmatrix} A_i+B_j & A_i +B_{j+1} & A_i + B_{j+2} & \cdots & A_i + B_{j'} \\ A_{i+1}-A_i & \cdots & \cdots \\ A_{i+2}-A_{i+1} & \cdots & \cdots \\ \vdots \\ A_{i'} - A_{i'-1} & \cdots & \cdots \end{pmatrix}

再对行作差分,可以得到

(Ai+BjBj+1BjBj+2Bj+1BjBj1Ai+1AiAi+2Ai+1AiAi1)\begin{pmatrix} A_i+B_j & B_{j+1}-B_j & B_{j+2} - B_{j+1} & \cdots & B_{j'}-B_{j'-1} \\ A_{i+1}-A_i & \cdots & \cdots \\ A_{i+2}-A_{i+1} & \cdots & \cdots \\ \vdots \\ A_{i'} - A_{i'-1} & \cdots & \cdots \end{pmatrix}

所以所求的 gcd\text{gcd} 等于
(Ai+Bj)\displaystyle (A_i + B_j)
(Ai+1Ai,Ai+2Ai+1,,AiAi1)\displaystyle (A_{i+1}-A_i, A_{i+2}-A_{i+1}, \cdots, A_{i'}-A_{i'-1})
(Bj+1Bj,Bj+2Bj+1,BjBj1)\displaystyle (B_{j+1}-B_j, B_{j+2}-B_{j+1}, \cdots B_{j'}-B_{j'-1})

后面两部分,可以用线段树维护 A,BA, B 的差分数组,开两个线段树
然后用线段树的 [i,i1],[j,j1][i, i'-1], [j, j'-1] 区间来求解
其中差分数组 A[i]=A[i+1]A[i]A'[i] = A[i+1]-A[i]

Codeforces Round 778

D. Potion Brewing Class
题目大意,给定一个正整数序列 AnA_n,并且给定 n1n-1 条关系
每一条关系用 aiaj=xy\displaystyle \frac{a_{i}}{a_j} = \frac{x}{y} 来描述,保证已知 aa 中任意一个元素
可以推出其他元素

对于所有可能的序列 AA,求出最小的 ai\sum a_i 并对 998244353998244353 取模

算法分析

如果已知 a1a_1 要推出 aka_k 怎么办,可以根据如下关系

ak=a1a2a1a3a2akak1\displaystyle a_k = a_1 \cdot \frac{a_2}{a_1} \cdot \frac{a_3}{a_2} \cdots \frac{a_k}{a_{k-1}}

对上述式子加以分析,不难发现,对于一对关系 (a1,a2)(a_1, a_2),给边赋上权值 a2a1\displaystyle \frac{a_2}{a_1}

那么从 a1a_1 转移到 a2a_2 就可以通过 a1a2a1\displaystyle a_1 \cdot \frac{a_2}{a_1} 来完成

对于每一条关系,可以考虑建图,用 dfs 来求解

具体来说,aiaj=xy\displaystyle \frac{a_i}{a_j} = \frac{x}{y},先换成 ajai=yx\displaystyle \frac{a_j}{a_i} = \frac{y}{x}

然后再 (i,j)(i, j) 之间连一条边,边带上权值(用分数结构体表示) yx\displaystyle \frac{y}{x}

图建好了,那考虑怎么求解?

  • 11 节点出发 dfs\text{dfs}11 节点的权值赋为 11,对于 (u,v,e)(u, v, e),计算出 v=ue{yx}v = u \cdot \displaystyle e\left\{\frac{y}{x} \right\}
    然后将节点的权值化为最简分数,1pv\displaystyle \frac{1}{p_v}
  • 然后统计 u\sum{u},即每个节点的权值,此时直接用模 PP 意义下的乘法逆元相加即可
  • 最后答案再乘上 lcm(pv)\text{lcm}(p_v),保证每个节点的权值是整数

算法实现上的细节
为了让写起来更简单一些,可以预处理打表 xx 所有的因子 fac(x)\text{fac}(x),在遍历的时候,从 uvu \to v
统计因子在分母中出现的最大次数,从而统计 lcm\text{lcm}

不妨设 vuyxv \leftarrow u \cdot \displaystyle\frac{y}{x}

先遍历所有 yy 的因子 ffac(y)f \in \text{fac}(y),令 cnt[f]=1\text{cnt}[f] -= 1

模拟约简的过程 yx1x/y\displaystyle \frac{y}{x} \to \frac{1}{x/y},对于 xx 中所有的因子
ffac(x),cnt(f)+=1f \in \text{fac}(x), \quad \text{cnt}(f) += 1

这样就统计出了 1x/y\displaystyle \frac{1}{x/y} 的因子个数

另外我们要统计因子出现的最大个数,所以更新 cnt(f)+=1\text{cnt}(f) += 1 的时候
同时更新 mx(f)=max(cnt(f))\text{mx}(f) = \max (\text{cnt}(f))

最终计算 lcm\text{lcm} 的时候依次遍历每一个因子,mx(x)\text{mx}(x) 最多出现多少
就乘上几个 xx

E. Arithmetic Operations

题目大意,给你一个长度为 nn 的序列 AA,问最少修改几个数,使得 AA 变成等差数列

Codeforces Round #797 (Div. 3)

F. Shifting String

题目大意,给你一个置换,和一个字符串,问这个初始字符串经过几次置换,能够再变换成原来的串?

首先来看置换的一些性质
性质一
性质一,称为奇偶性,记一个置换序列,比如 (5,2,3,1,4)(5, 2, 3, 1, 4) 逆序对数目为 σ(n)\sigma(n)
置换的符号为 (1)σ(n)\displaystyle (-1)^{\sigma(n)}

性质二,置换的环分解
01

如果置换 TT 是一个大小为 nn 的环,那么 TkT^k 分解后是大小为 ngcd(n,k)\displaystyle \frac{n}{\text{gcd}(n, k)} 的环
并且这样的环有 gcd(k,n)\text{gcd}(k, n)

证明如下,对于环 TT,步长为 kk,可以沿着 TT 的轨道走
原来需要 nn 次走完,现在只需要 ngcd(k,n)\displaystyle \frac{n}{\text{gcd}(k, n)}

所以分解出来的新的环长度为 ngcd(n,k)\displaystyle \frac{n}{\text{gcd}(n, k)},并且这样的环有 gcd(n,k)\text{gcd}(n, k)

这样不断地轮换下去,每一个环对应原来序列 TT 中的下标
imodgcd(n,k)=0,1,2,\displaystyle i \bmod \text{gcd}(n, k) = 0, 1, 2, \cdots 的元素组成

T=(1,3,5,2,4,6)T = (1, 3, 5, 2, 4, 6)
T2=(1,5,4)(2,6,3)T^2 = (1, 5, 4)(2, 6, 3),其中 gcd=2\text{gcd} = 2,所以每个循环下标是
按照 mod2\bmod 2 的余数,对下标进行分类
[0]:{0,2,4},[1]:{1,3,5}[0]: \{0, 2, 4\}, \quad [1]: \{1, 3, 5\}
所以分解成 T2=(1,5,4)(3,2,6)T^2 = (1, 5, 4)(3, 2, 6)

继续轮换下去,还可以发现 T7=TT^7 = T
所以一定有 Tk=T(k1)modn+1\displaystyle T^k = T^{(k-1) \bmod n + 1}

性质三
gcd(n,k)=1\text{gcd}(n, k) = 1 时的情形
同样令 a=T,a=Tka = T, a' = T^k,初始时,有 a[0]=a[0]a[0] = a'[0]
然后开始考虑沿着轨道走,步长为 kk,这样就有 a[i]=a[ikmodn]a'[i] = a[ik \bmod n]
注意下标从 00 开始

有了这些置换的性质之后,这个问题就简单了

  • 首先对该置换进行环分解,cycle[i]\text{cycle}[i] 这个 vector\text{vector} 表示第 ii 个环是哪些数的下标
  • 然后我们考虑第 ii 个环,假设第 ii 个环中有 (1,3,5)(1, 3, 5) 这个元素,我们就把 s1,s3,s5s_1, s_3, s_5 单独拿出来
    t=s1s3s5t = s_1s_3s_5,看看走多少步之后,(假设是 kk 步),使得 ttt \to t

具体到算法实现上,可以先对置换 TT 生成的所有串打表
T0=sT^0 = s,然后依次打表 {T1T2,,Tk}\{T^1,T^2, \cdots, T^k\}
可以写一个 gen\text{gen} 函数来实现:根据置换生成串

对于每一个循环 ii,从 j[1k]j \in [1\to k] 检查,将 TkT^k 中和 cycle(i)\text{cycle}(i) 相关的下标取出来,记为 tkt_k
同时把 T0T^0 相关下标取出来,记为 t0t_0,如果 t0=tkt_0 = t_k
那么很显然循环 ii 的幂指就是 mi(i)=jmi(i) = j

最后求 lcm(mi(i))\text{lcm}(mi(i)) 就是答案

G. Count the Trains
题目大意,铁轨上有 nn 节车厢,每节车厢可以达到一个最高速度,记录在一个序列 {ai}\{a_i\}
车厢从左到右编号是 1n1 \to n

现在让车厢向左,并且尽可能快地开动,要求靠右的车厢实际速度不能超过靠左的车厢
这样形成了若干段速度一致的数节车厢,称为一节火车,比如序列 a[10,13,5,2,6]a[10, 13, 5, 2, 6] 对应车厢运行速度为
[10,10,5,2,2][10, 10, 5, 2, 2] 形成了 33 节火车

车厢行驶时,给你 mm 条形如 (k,d)(k, d) 信息,表示第 kk 节车厢最高速度下降了 dd
需要维护这个过程中火车的总节数

算法分析
可以利用线段树,考虑维护区间内有多少节车厢 cntcnt
对于区间合并,用 sufsuf 表示区间后缀中的最后一个数是多少,prepre 表示前缀的第一个数,那么

  • l.suf>r.prel.suf > r.pre 左右两区间不能合并,此时 cnt=l.cnt+r.cntcnt = l.cnt + r.cnt
  • l.sufr.prel.suf \leqslant r.pre 左右两区间可以合并,cnt=l.cnt+r.cnt1cnt = l.cnt + r.cnt - 1
  • 然后维护 pre,sufpre, suf,只需要 pre=l.pre, suf=r.sufpre = l.pre, \ suf = r.suf

接下来是修改操作,注意本题给出的车厢速度和实际速度并不相同,实际速度是单调递减的
线段树应该维护的是车厢的实际速度,对于修改操作,a[x]a[x]da[x] \leftarrow a[x] - d
可以在线段树上找出 xx 这个叶节点的实际速度 vv,修改后 v=min(v,a[x]d)v = \min(v, a[x]-d)

那么对于实际速度序列来说,我们需要在线段树上找到第一个 <v< v 的数的位置 rr
区间 [x,r)[x, r) 的速度都应该执行线段树区间修改,改成 vv

线段树上找到第一个 <v< v 的数,二分逻辑
需要维护区间的最小值 minvminv,如果 l.minv<vl.minv < v,那么去左子树中找,否则去右子树

方法二,珂朵莉树
注意到列车的实际速度是非严格单调下降的,我们仿照珂朵莉树
用一个二元组 <id,v>\left<id, v \right> 来表示列车实际运行速度

  • 第一次插入 xx 的时候,如果 prev(it).vx\text{prev}(it).v \leqslant x,那么 xx 是没有用的
    就把 xx 删掉,这样就保证了初始序列是严格单调减的
  • 然后考虑将 a[i]=da[i] -= d,实际上可以暴力地来做
    就是先令 a[i]=da[i] -= d,然后插入二元组 <i,a[i]>\left<i, a[i] \right>
    这里需要维护好单调性,就是插入结束后,从刚插入的位置 itit 开始,到 set\text{set} 的结束
    所有的值都必须 <x< x,凡是 x\geqslant x,我们就把这个元素删掉
  • 这样做的复杂度是 O((n+m)logn)O((n+m) \log n) 的,因为任何一个元素只会被删除 n+mn+m

AtCoder Beginner Contest 255

D - ±1 Operation 2
题目大意,给你一个序列 A=<a1,a2,,an>A= \left<a_1, a_2, \cdots, a_n \right>,一次操作中,你可以对任意一个数 +1,1+1, -1
QQ 个询问,一次给你一个数 xx,问将 AA 中所有数都变成 xx,所需要花费的最小代价

算法分析
这个问题实际上就是问 S=i=1naix\displaystyle S = \sum_{i = 1}^{n} |a_i - x| 的最小值
可以先对 aia_i 排个序,这样就存在 a1a2ana_1 \leqslant a_2 \cdots \leqslant a_n

这样的话,SS 就可以分段表示,只需要知道第一个满足 aj>xa_j > x 的位置 jj,那么 SS 就可以表示成

i=1j1(xai)+i=jn(aix)\displaystyle \sum_{i = 1}^{j-1} (x-a_i) + \sum_{i = j}^{n} (a_i - x)

leetcode 杂题

1723. 完成所有工作的最短时间
根据数据范围 1k121 \leqslant k \leqslant 12,不难想到可以用状态压缩来做
具体来说,就是枚举子集

ii 个工人的任务分配状态,可以从第 i1i-1 个工人转移过来
定义 dp(i,st)dp(i, st),表示对 ii 个工人分配任务,已分配任务的状态是 stst 时,集合的最大值最小是多少

枚举 stst子集 pp,那么存在 i1ii-1 \to i 的转移,对应第 ii 个人分配到的任务,状态压缩成 pp
先计算出 pp 中有哪些工作是一定要做的,这些工作的完成时间记为 sum\text{sum}
然后计算出 stst补集 qq,将 pq=stp \cup q = st 的过程中,存在状态转移

dp(i,st)=minpst, q=stp( max(dp(i1,q),sum )\displaystyle dp(i, st) = \min_{p \in st, \ q = st-p}\left(\ \max( dp(i-1, q), \text{sum} \ \right)

最后 dp(k,(1n))dp(k, (1\ll n)) 就是答案