Atcoder abc217
G - Groups
https://atcoder.jp/contests/abc217/tasks/abc217_g
有数字1..N
把它们分成k组(每组至少一个数)
要求, 每组中没有两个数 mod M 是相等的
问对于k=1…n 分别有多少方案
答案 模 998244353
方案: 如果两方案不同,至少有一个(x,y) 在一个中同组,另一个是不同组
范围
n 5000
2 <= m <= n
我的思路
显然 (n - 1) / m + 1 个 mod m = 1 的
我们也容易计算mod m = r 的有多少个
然后 计算这个方案数无非是两个方向, 正向算和逆向算
正向算, 则需要对每个方案唯一标识, 那么考虑用每组中( (value - 1 )mod m, value) 最小的作为组标识
似乎相互依赖很多, 不知道怎么算
反向算, 则就是同 mod的放在同一个位置了
题解
我感觉自己已经傻掉了, 看到n 5000 竟然没有想一下n方的算法
dp[i][j]
表示前i个数,分成了j组方案
如果没有同余限制
dp[i][j] = dp[i-1][j-1](单独放在j) + j * dp[i-1][j] (前面i-1已经放了j)
个
注意到同余的限制
那么j的放法就是 和它不同余的位置
已经有 ((i-1) / m)个和它同余了
dp[i][j] = dp[i-1][j-1](单独放在j) + (j-(i-1)/m)保证非负 * dp[i-1][j] (前面i-1已经放了j)
个
就没了
代码
https://atcoder.jp/contests/abc217/submissions/33750183
1 |
|
H - Snuketoon
https://atcoder.jp/contests/abc217/tasks/abc217_h
初始在点0, 每秒可以-1,0,+1
N次事件: 在ti时刻, 若在Xi左侧且Di = 0,受到和Xi距离的伤害, 若在Xi右侧, 且Di = 1,受到和Xi距离的伤害
想要受到伤害最小化, 求最小值
范围
N 2e5
ti [1,1e9]
di 0/1
Xi [-1e9,1e9]
我的思路
看起来像dp
题解
$dp_{i,x} = $ 在Ti分钟 恰好在x 所需要受到的最小伤害, 为了方便认为T0 = 0
那么
dp[0][0] = 0
dp[0][!=0] = INF
若Di = 0, dp[i][x] = min(dp[i-1][y]) + max(0,Xi - x)
, 且|y-x| <= T[i] - T[i-1]
若Di = 1, dp[i][x] = min(dp[i-1][y]) + max(0,x - Xi)
, 且|y-x| <= T[i] - T[i-1]
很明显直接算会TLE
对于一个具体的i, 在二维平面上画点$(x,dp_{i,x})$, 其中横坐标范围是$[-T_i,T_i]$, 然后用线段连起来, 发现是个凸函数(下凸)
证明: 若对于i-1是下凸函数, 注意到min(dp[i-1][y])
依然是凸函数, 而max
部分也是凸函数,所以对于i
也是凸函数
因此问题变成维护那些拐点
每次min的操作,相当于把最小值的区间 向两侧 平移T[i]-T[i-1]
注意到 最初是[0,0], 其实就是把区间最左和最有移动到-Ti
和正Ti
然后每次+max, 相当于一段不变, 一段 斜率+1, 还可能产生新的节点
如何维护呢?
注意到每次 min的过程核心等于向两侧平移, 如果以 和-Ti
,Ti
的距离来看, 甚至是没有变化
每次 +max, 是区间内斜率增加1, 那不妨直接记录 斜率1的起始点,斜率2的起始点, 斜率3的起始点( 可能会有重合, 这些点和左侧的距离, 和右侧的距离
-3 -2 -1 0 1 2 3 这样的斜率区间
如果加上 0 1 的
最终会变成 -3 -2 -1 0 1 2 3 4 这样的, 虽然可能有起点重合(斜率区间长度为0
代码
https://atcoder.jp/contests/abc217/submissions/33754555
1 |
|
总结
G
干啊, n方dp都想不到了,我
据说有数学的 N log N 的方法
H
知识点就是凸函数, 但是变化点较少时, 只需要维护这些点即可
然后维护的时候, 想办法保持尽可能多的不变量, 让变化记录是常数级别