Educational Codeforces Round 135
https://codeforces.com/contest/1728
F. Fishermen
n个钓鱼人
第i个获得大小a[i]的鱼
他们选一个报告的顺序, 报告鱼的大小b[i], 但是可能虚报
第一个报告的 诚实报告
其它人报告的 是最小的b[i]=a[p[i]]的倍数 且 大于 前一个报告值
1 | 1 2 3 2 2 8 3 |
对于所有报告顺序, 让所有报告的 sum b[i] 最小
输出sum b[i]的最小值即可
范围
n 1000
ai [1,1e6]
6s
512mb
我的思路
如果两两不同, 那么从小到大就好了, 因为b[i] >= a[i], 这是所有都取a[i] 是最小值
如果 两个相同, 那么至少一个要虚报, 那么至少 2a[i]
如果 三个相同, 那么=> a[i], 2a[i], 3a[i]
但不只是让它们不同就完了, 因为翻倍后依然可能相同
1 | 2 2 2 3 3 |
1 | 2 4 6 3 6 |
这两个6还要处理
但是一个6是变8, 一个是变9
显然 如果a 有重复n次, 那么 a,2a,…,na 一定都有的,不会中间空着, 否则把更大的来填补可以让它更小
感觉上 当倍增的时候遇到相同的, 那就让小的去增加,
但实际上
1 | 2 2 2 2 3 3 |
如果是 2的6去变化, 那么要变成16, 不如3的6变成9
而变化, 因为上面说了不会中间空着, 那就是每次找 最大的倍数去变
这样靠差值最小来变化的贪心, 似乎是对的吗???? 但我证明不了
然后 如果这样搞, 运算量
1 | 600 个1, 300个2 这样大量重叠 |
似乎也就O(2n)?
写了下, 不出所料 样例都没过
1 |
|
1 | 1 -> 1 |
问题就在6的地方是动2还是动3, 动2是先到8再到10, 这个8 能预判吗??
并不会
题解
b是可能的
的答案 的充要条件 是 b[i] 是 a[i] 倍数, 且b[i] 两两不同
bi <= n * a[i]
, 因为 每个=a[i]数之所以要乘法, 就是为了必让和其它数碰撞,那么 不妨设其它数变化固定了, 那么最多占了 n-其它的个数 个位置, 所以剩下的位置 不小于 =a[i]的个数, 所以显然b[i] <= n*a[i]
然后构建二分图???!!!
左侧 a[i] 右侧 (1~n) a[i]
然后是二分图 带权匹配, 还要权最小
但是最小费用流 跑 1e6 点, 1e6边的图 并不可行
按照右侧点从小到大排序
然后把左右交换!!!!, 用b 去配a, 而不是a去配b
这样就是从小到大去配, 而且配了的, 即使交叉也是保持配了的
如果配失败了, 不需要清空vis ,可以优化O(n^4) 到 O(n^3)
代码
https://codeforces.com/contest/1728/submission/189159290
1 |
|
G. Illumination
线段[0,d]内,有n灯, m个点关键点
对于每个灯, 可以选择它的 power \in [0,d], 点x上i的灯, 可以照亮 [x-power[i], x+power[i]]
一个合法 power 方案, 要每个关键点至少被一个灯照亮
q个询问, 每个询问独立,
- 如果 在 qi 位置 增加一个灯, 问添加后所有灯的所有合法power的方案数 mod 998244353
范围
d [4,3e5]
n [1,2e5]
m [1,16]
q 5e5
8s
512mb
我的思路
m有点小, 但如果bitmask 再乘上n又有点大了
这样每次独立的增加一个, 感觉上有点像区间维护 中间插东西, 需要线段树一类的去维护
这q 5e5 感觉乱给的, 毕竟d才3e5, 不过都是3e5的量级 也差不多
如果不是插入一次性给的点, 怎么算
dp[i][j] =
前i个点, 成功覆盖小于 pos[i] 的 关键点 超出距离为j的 方案数
这样 状态 是 O(n * d) 太大了, 还要转移就更不行了
dp[i][j] =
, 第i个关键点 左侧覆盖它 且index 最大的点是j, 且比i小的关键点全被覆盖的方案数
感觉上信息不足, 超出的距离也有意义 需要记录, 而且还可以从右侧覆盖
直接按照m个点切割成 m+1段区间
计算 区间i内的点, 向左覆盖到最小的 关键点
count[区间i][向左覆盖到最小的关键点][向右覆盖到最大的关键点] =
方案数
然后就可以
dp[前i个区间中的点][向左][向右] = 方案数
ans = dp[m+1][1][m]
两个问题 count 如何计算, dp如何转移
dp[i][l][r] = sum dp[i-1][l0][r0] * count[i][l1][r1]
满足 (min(l0,l1) = l, max(r0,r1) = r)
似乎好像 就是 [16x16] 的转移, 那么就是 其实就是 矩阵乘法+线段树维护? 但256^3 再 乘上q(5e5)实在有点大
这q甚至似乎 256 * 256 都接受不了
这个q显然可以离线按照顺序算
所以在离线的加持下问题变成3段(或2段)
左[l][r] , 当前[l][r], 右[l][r]
要求它们组合情况下 [1][m]
的值
其实问题回到 上面提出还未解决的问题, count怎么算
再仔细一想, 其实不用切割,
每个点可以表示成 [不覆盖关键点 的方案数, 覆盖1个的方案数….,覆盖16个的方案数]
最多17个状态
而实际上 变成这个状态后, 与实际的点在哪里 就属于冗余信息了.
那么就是 state[l][r] x point[17个状态] => new state[l][r]
要做~7e5次这个运算, 因为5e5(3e5)次都是单独的, 所以n(2e5)次是批量还是单独 似乎对量级都影响不大了
感觉表达式一写,它就是一种二维的什么卷积??
c[i][j] = sum a[i0][j0] * b[i1][j1] , (i,j) = (min(i0,i1),max(j0,j1))
统计一点
i0=i,j0=j,i1>=i, j1<=j
i0=i,j0<j,i1>=i, j1==j
i0>i,j0=j,i1==i, j1<=j
i0>i,j0<j,i1==i, j1==j
配上二维前缀和, 可以优化到16 * 16, 似乎就能过了, 256*7*10**5= 179200000
但实际上是不对的, 当两个不重叠的 [l,r] 合并 并不是 min, max
而还是 bitmask
f[bitmask] = 方案数
f[bitmask] * 新点 => newf[bitmask]
这就不一样了, 需要得到初始的f[bitmask]
和 f[bitmask] * 新点 的 [1<<m-1]
的单点值即可
单点 最多17种非0 bitmask, 但是17 * (1<<m-1) * q
也接受不了
所以问题在于, 如何批量计算初始的f[bitmask]
和 单点如何快速计算 [1<<m-1]
的值
看起来前面是 c[i] = sum a[i0]*b[i1], i=i0|i1
的卷积问题
但即使 能够 线性(比log还小)得到, 2**16*2*10**5= 13107200000
, 似乎也不太能做
而最后的 单点计算反而简单一些, 如果前面算好了, 可以把bitmask 转换成 [左侧连续1的最右][右侧连续1的最左]
再累和,去预处理 [左侧连续1 >= i][ 右侧连续1 <= j]
的个数
这样 单点的p 加入, 枚举17种 都是O(1)的了
所以如果能快速算出前面的f[bitmask]这个题就AC了
题解
也是先考虑静态
但它不是考虑的插入计算, 而是考虑 所有 - 无效的方案, 然后无效的方案用容斥算
容斥就很显然, 灯的照亮半径被左右 指定 无效的关键点限制最大长度, 所以 算完后 ans=ways[mask]*(-1)^popcount(mask)
而且容斥, 正好和我上面切割的想法一样, 因为 当指定相邻 i 和j不被照亮时, 它们中间的 对 ways贡献的倍数就是固定的
所以容斥 先计算 count[i][j] =
, i和j之间的方案数, 复杂度为 (<= m左关键点 * m右关键点 * n之间的点), 是可以接受的
然后再转成 dp[前缀mask] = 方案数, 来帮助计算 ways[mask] = O(mask * m) 也是可以接受的,
O(m) 每个询问??
1 | 11 3 2 |
1 | 11 |
这里通过 pways[mask] = 钦定mask时 一个点的方案 * 距离 和 pways[mask 更大一个点] * 1/距离来实现
比如 对于灯L=11 和 mask=10,
ways[10] = pways[10] * pways[11]
而关键点P=7时, 它对 11的贡献是 *4
, 而对于10
的贡献是*1/4
关键点P=2对10
的贡献是*9
,对11
的贡献是*2
, 所以 ways[10]=18
1 | ways[00]=1 // 这个会被替换掉 |
ans=sum ways[mask] * (-1)^popcount(mask)
ans=sum (\prod f(mask,l[i])) * (-1)^popcount(mask)
ans=sum (\prod f(mask,l[0..n-1])) * f(mask,x) * (-1)^popcount(mask)
所以需要 预计算 让f(mask,x) == d 的所有mask对应的 sum (\prod f(mask,l[0..n-1])) * (-1)^popcount(mask)
这样 一个灯x的和最近点j距离d的贡献, mask = 是msk[x][j]含j的子集 且 不是msk[x][j]不含j的子集
所以 按照accways[mask]=sum ways[mask] * (-1)^popcount(mask)
即可
转换加权类和
1 | accways[00]=1728 |
这样 ans = sum d[x][j] * (accways[mask[x][j]] - accways[mask[x][j] - (1<<j)]) , j=0..m-1
代码
https://codeforces.com/contest/1728/submission/189262201
1 |
|
总结
这F全场9人过, G全场48人过
F
光是b[i] <= n * a[i]
就没想到, 所以也完全没往二分图去想, 不过感觉 a->b 配对的 似乎就是 网络流和二分图相关的, 要建立联系
然后这个把值反过来 和 二分图匹配时的性质, 来保证最小性 也是学到了, 有点意思!!!
G
思路流程还是感觉比较传统了, 都是先考虑静态, 再考虑插入
这里 的 乘d 和 d的相反数, 感觉逻辑懂了, 但是没有很悟, 感觉还是很神奇, 因为我自己想的时候, 总想到 一个精确的[d0~d1]之间
感觉是 ways[mask] = f(点1) * f(点2) *...
然后f(点1) = p(短距离=大mask) * p(短距离的逆=相对小mask) * ... * p(正好距离=mask)
, 题解里的(sum-over-subsets (well, product-over-subsets in this case :)))
以及这里 关于相等的经典处理, 就是按照额外顺序来让它们全部都不等
然后这里反复的子集集合的变化, 虽然都可以证明, 但还是很不熟悉