AtCoder 瞎做

ARC 058

E - 和風いろはちゃん / Iroha and Haiku

题意

有一个长为 (n) 的序列 (a_0,a_1,cdots, a_{n - 1}) ,给你三个参数 (X,Y,Z)

定义一个好的序列满足 存在 (0le x < y < z < w le n) 使得

[egin{align} sum_{i = x}^{y - 1} a_i &= X\ sum_{i = y}^{z - 1} a_i &= Y\ sum_{i = z}^{w - 1} a_i &= Z end{align} ]

问满足条件的序列有多少个。(对于 (10^9 + 7) 取模)

(3 le n le 40, 1 le X,Z le 5, 1 le Y le 7)(1 le a_i le 10, a_i in mathbb{N^{+}})

题解

一道很妙的题qwq

一开始觉得是个思博题,然后发现怎么正向计数都会算重。

后来去看了下题解,原来可以转化成 (10^n -) 不合法的序列数,这样就不会记重了。

但这样转化后如何记呢?数据范围十分地小,可以状压 (dp)

具体来说就是把一个 (a_i) 拆成一个 (1)(a_i - 1)(0)

比如 (5) 会被拆成 (underline{10000})

然后我们就可以把之前填的数顺次拼起来了。

这样有什么好处呢?不难发现如果一段连续的和为 (S) ,那么 (S) 转化后的集合 会是 连续一段转化后拼起来构成的集合的 子集

比如 (2 + 4 = 6) ,就有 (underline {100000} in underline{10} + underline{1000} = underline{101000})

那么我们只要对于每个位置状压前 (X + Y + Z = 17) 位就行了,一开始 (X,Y,Z) 压成的状态顺次拼到一起的状态,就是所有不可行的状态的自己。

枚举当前填到哪一位,以及状态,再枚举这位填什么就行了,复杂度就是 (O(10n2^{X+Y+Z})) 的。

技巧

要求连续和很小的时候也可以巧妙状压,注意看数据范围。

代码

戳这个 链接 ,好写的很。

ARC 059

F - バイナリハック / Unhappy Hacking

题意

一开始会有个空串 (S) ,有三个操作。

  • (S) 后面填个 (0)
  • (S) 后面填个 (1)
  • 删去 (S) 的最后一个字符,如果 (S) 为空,那么不会改变。

然后你会进行 (n) 次操作,给你最后的串 (S) ,问有多少种不同的操作序列满足条件。(对于 (10^9 + 7) 取模)

(n le 5000, 1 le |S| le n)

题解

一开始随便想了个 (O(n^3)) 的暴力,就是令 dp[i][j][k] 为当前进行了 (i) 次操作,串长为 (j) ,第一个失配的地方在 (k) 的操作方案数,对于当前进行什么操作分开考虑就行了。

其实正解很思博,我们之前那个 dp 第三维是没有必要的,因为我们发现串的形态是不会影响答案的,所以就令 dp[i][j] 为进行了 (i) 次操作,串长为 (j) 的方案数。

最后答案就是 (displaystyle frac{dp[n][len]}{2^{len}}) ,因为最后每个串对应的方案数都是一样的,总共有 (2^{len}) 个串,这样就是 (O(n ^ 2)) 的了。

其实可以继续优化,怎么考虑计数呢,因为最后的串长要恰好为 (len) ,我们可以转化成至多为 (len) 的答案减去至多为 (len - 1) 的答案。

我们假设 (f(x)) 为串长至多为 (x) 的答案,那么表达出来就是:

[f(x) = sum_{i = 0}^{n} [i - (n - i) le x] ({n choose i} - {n choose i - (x + 1)}) imes 2^{i} ]

也就是我们考虑在 (n) 个操作中有 (i) 个操作为在后面添加字符,剩余 (n - i) 个操作为退格操作。

  • ([i - (n - i) le x]) 意味着所有退格和添加字符操作 全部抵消 后串长不超过 (x) ,这是必要的条件。

  • (displaystyle {n choose i}) 是所有可能添加和退格操作的排列,(-displaystyle {n choose i - (x + 1)}) 是所有最后串长会超过 (x) 的情况。

    为什么会出现超过 (x) 的情况呢?因为会有好一些退格操作都不会产生作用,不会产生作用的如果有 (i - (x + 1)) 及以上个,那么就是不行的。

    不难发现,把其中不会消除的 (i - (x + 1)) 退格操作放在排列在 (n) 个总操作之中的任意一个地方,对应仅对应且仅对应一组不合法的解。

  • 最后发现每个添加数字的操作会对应两个操作,所以还要乘上 (2^i)

预处理了阶乘和阶乘逆元后就可以做到 (O(n)) 的复杂度啦。

技巧

对于求最后要求形态唯一的题,可以考虑有多少个形态的方案数是和它一模一样的,最后除去那么多就行了,通常可以降低一维的复杂度。

代码

(O(n^3):) 代码戳这里

(O(n^2):) 代码戳这里

(O(n):) 代码戳这里

ARC 060

D - 桁和 / Digit Sum

题意

给你两个数 (n, s) ,找出一个最小的数 (b) 使得 (n)(b) 进制下的数位和为 (s)

(n,s le {10}^{11})

题解

对于 (< sqrt n)(b) 我们可以暴力枚举,然后逐位拆解求和,复杂度是 (O(sqrt n log n)) 的。

对于 ([lceil sqrt n ceil, n])(b) ,那么 (n)(b) 进制下只有两位,其实就是解一个如下的方程:

[egin{align} lfloor frac{n}{b} floor + n mod b &= s \ lfloor frac{n}{b} floor + (n - lfloor frac{n}{b} floor imes b) &= s\ lfloor frac{n}{b} floor (1 - b) &= s - n \ b &= frac{n - S}{lfloor frac{n}{b} floor} - 1 end{align} ]

不难发现 (displaystyle lfloor frac{n}{b} floor) 只有 (sqrt n) 种取值,我们直接整除分块,然后解方程即可。

然后对于 (n = s) 的点需要特判,(b) 此时为 (n + 1)

所以最后复杂度是 (O(sqrt n log n)) 的。

技巧

对于进制下的题,要么数位 (dp) ,要么考虑分情况讨论。

(ge lceil sqrt n ceil) 的进制只会有两位,(< lceil sqrt n ceil) 的进制位也只有 (log n) 层。

代码

这里

F - 最良表現 / Best Representation

题意

定义一个内部不包含循环子串 (S) 至少出现两次的串为好串,比如 (underline{a}) 为好串,(underline{abab}) 不为好串,(underline{cdcdc}) 为好串。

给你一个字符串 (w) ,现在要把它划分成很多段好串,其中段数要尽量少。然后问段数尽量上的前提下,问有多少种划分方式。(对于 (10^9 + 7) 取模)

题解

假设最好最优的段数为 (l)

判断一个串是否为好串可以先用 (kmp) ,求出每个前缀的 (fail) (也就是这个前缀的最长公共前后缀 ((border))

然后就可以用一个常用性质了:

最小循环串(不一定会出现完毕)的长度等于 (len - border) ,具体证明可以参考 这里

然后它为好串,当且仅当 (fail_i)(0) 或者 (i mod (fail_i - i) ot = 0) 。前者因为最小循环串是自己本身,后者因为不能完全填满。

首先可以特殊考虑两种情况:

  • (l = |w|:) 此时所有字母都是一样的,可以在最后判断。
  • (l = 1:) 此时整个串可行,可以直接判掉。

剩下的情况其实答案都为 (l = 2) ,为什么呢?

因为你可以考虑把 (1 sim |w| - 1) 分到一组,最后一个字母单独分成一组。

如果这样不可行的话,只有两种情况:

  • 全部字母相等,前面会特判掉。
  • (1 sim |S| - 1) 存在一个最小循环串 (|S|) 至少出现两次,和最后一个拼起来后一定使得整个串不存在一个可行的最小循环串。

然后剩下计算方案数,只需要枚举断点,然后判断前后缀是否为好串即可。( (kmp) 预处理两次就行了)

技巧

牢记 (border(fail)) 与 最小循环串 互相转化的性质。

代码

这里

ARC 063

E - 木と整数 / Integers on a Tree

题意

给你一颗有 (n) 个点的树,一开始有 (k) 个点有权值。问是否能让其他的点也赋上权值,使得相邻两个节点的权值差的绝对值为 (1)

如果可以,请给出一组构造方案满足条件。

(1 le k le n le 10^5)

题解

首先分层考虑奇偶性是否存在解。

然后你对于每个点维护它可取的权值范围 ([l_i, r_i])

然后每次需要从儿子转移上来:

[l_u = max{l_u, l_{son} - 1}\ r_u = min{r_u, r_{son} + 1} ]

边界就是,这个点无权值就是 ([- infty, infty]) ,有权值就是 ([val_u, val_u])

如果 (l_u > r_u) 无解。

然后只需要从上往下随意构造一组方案即可 。

技巧

树上构造关于权值构造可以考虑奇偶性。关于差值,可以考虑每个点的取值范围然后向上转移即可。

代码

这里

ARC 065

E - へんなコンパス / Manhattan Compass

题意

平面上有 (n) 个点,其中选出两个点 (a, b) ,令他们之间的曼哈顿距离为 (D)

两个点联通当且仅当这两个点之间的曼哈顿距离也为 (D) ,最后询问 (a) 所在联通块的每个点的度数之和。

(1 le n le 10^5)

题解

一道挺不错的套路题。

看到曼哈顿距离不难想到可以与切比雪夫距离进行转换。

切比雪夫距离:

平面上两点 ((x_1, y_1), (x_2, y_2)) 之间的距离为 (max(|x_1 - x_2|, |y_1 - y_2|))

如何转换呢?考虑把原来坐标系旋转 (45^{circ}) 。原来的坐标 ((x, y)) 就变成了 ((x + y, x - y))

然后原图上两点的曼哈顿距离就变成切比雪夫距离了。

来理性地证明一波:

假设原来两点为 ((x_1, y_1), (x_2, y_2)) 变化后就是 ((x_1 + y_1, x_1 - y_1), (x_2 + y_2, x_2 - y_2))

[egin{align} d_{mathcal{Chebyshev}} &= max{{|(x_1+y_1) - (x_2 + y_2)|, |(x_1 - y_1) - (x_2 - y_2)|}}\ &=max{{|(x_1-x_2) + (y_1 - y_2)|, |(x_1 - x_2) - (y_1 - y_2)|}}\ &=max{{|(x_1-x_2) pm (y_1 - y_2)|}}\ &=|x_1 - x_2| + |y_1- y_2| end{align} ]

注意最后一步可以自己讨论两边的正负来证明。

然后转了后,我们就可以转化成对于两个点其中一维的差值刚好为 (D) ,另外一维 (le D)

为了方便讨论,我们先假设 (x) 差值为 (D) ,另外的一维也是一样讨论。

[egin{cases} x_1 - x_2 = D\ -Dle y_1 - y_2 le D end{cases} ]

我们固定一维 (x) ,那么另外一维 (y) 就处于一个范围内,我们就可以快速处理出一个点的度数为多少了。

具体实现就是按 (x) 排序, (x) 相同按 (y) 排序,然后用两个指针 (l, r) 维护当前的区间 ([l, r]) 就行了。(因为区间是单调的)

但是连边肯定不能暴力连,但我们发现是处于一段连续区间内,是否可以线段树优化建边呢?是可以的,但完全没有必要。

因为最后我们只需要求联通块,只需要保证连通性不会变,那么每次连边的时候我们发现相邻的不需要重复连边没有意义,我们每次记个 (pos) 表示当前连边的区间扫到哪了就行了,然后这个点连向最后一个就行了。

这样的话最后的边数是 (O(n)) 的了,最后总复杂度是 (O(n log n)) 的,瓶颈在排序上。

技巧

曼哈顿距离常常能和切比雪夫距离转化,然后常常定下一维另外一个就是个区间范围,这样就好处理很多了。

然后区间连通性连边可以用操作把边数降到 (O(n))

代码

这里,挺短的。

原文地址:https://www.cnblogs.com/zjp-shadow/p/9851787.html