Atcoder abc214
G - Three Permutations
https://atcoder.jp/contests/abc214/tasks/abc214_g
给[1..N]的排列p和q
求多少个排列r 满足 r[i] != p[i] , r[i] != q[i], i = [0..N]
mod 1e9+7
限制
n 3000
题解
我的思路
如果只是给一个排列p
要找另一个排列r让每个位置对应不等(r[i] != p[i])
一个想法是, 把它直接按照p[i]的1到n重新排序
问题变成了 找r[i] != i的排列方案数
考虑长度n的和n-1之间变化
如果i放的n,而n放的i ,那么 去掉i和n, 方案数 为f(n-2)
n 有 n-1中交替放的方案, (n-1) f(n-2)
如果i放的n,而n放的不是i, 那么,交换i和n放的, 前n-1也合法, f(n-1)
f(n-1) 每个方案每个位置和n换, 贡献(n-1)f(n-1)
f(n) = (n-1)(f(n-1) + f(n-2))
f(1) = 0
f(2) = 1
f(3) = 2(1+0) = 2
那么对于两个序列
首先一样的思路按照p 来排序
那么变成 r[i] != q[i], r[i] != i
但因为q[i]的限制并不能 像上面那样转移
题解
如果对于每个位置,计算ri=pi或ri=qi的排列方案数,可以考虑容斥
假设 i1,i2,…ik, 对应的下标满足, ri=pi 或 ri=qi, 那么要计算所有r[i1],r[i2]..r[ik]的值方案
对于每个 i in i[1..k],在图中,我们增加一个(pi,qi)的边, 只需计算每条边分配给其一个端点的总数,以便没有两条不同的边共享一个分配给它的顶点。(意思就是边即是i, 而给边分配的点,即是r[i]的值, 不能共享点,意味着值不重复
(注意到 如果(pi,qi)链接的话, 只可能是 链 或 环,不可能出现分叉
对于每个联通分量考虑(除去孤立点和自环)
因为环之间两两不相关, 所以每一组i的选择答案 = 不同环的方案的乘积
我们对于一个K个点的环内, 选了k条边, 的方案数
当所有边被选(所有的i都有相等关系), 那么有2种方案
不是全部都选, 考虑把环剖成链讨论首尾是否选择
dp[i][j][s0=0/1/2][si=0/1/2]
表示前i条边,选择了j条, 且第一条是s0 状态,第i条是si状态的方案数
0: 未选择
1: 该边分配了左点
2: 该边分配了右点
状态转移
不选 dp[i][j][s0][0] = sum dp[i-1][j][s0][0..2]
向左 dp[i][j][s0][1] = sum dp[i-1][j-1][s0][0..1]
向右 dp[i][j][s0][2] = sum dp[i-1][j-1][s0][0..2]
这样最后长n的环选了k条链的总方案数 就是sum dp[n][k][s0][s1], 且 (s0 != 1 || s1 != 2)
记为circle[n][k]
如果gi 表示 指定了i个不合法的选择, 剩余的n-i个任意选(可以合法也可以不合法,但始终满足是排列)
那么 $ans = \sum_{i=0}^n (-1)^i(n-i)! g_i$
而gi也可以通过上面环的结果, 去做dp
f[i][k] =
前i个环指定了k个边 的方案数
f[i][k] = sum{f[i-1][k-t] * circle[sz[i]][t]}
前i个环指定了k个边 的方案数
于是把所有环剖成链连续放在数组上
g[i][j][s0][s1]
= 前i边,指定分配了j条, i所在环的起始是s0,结束是s1的方案数, 这里也是把s0也与i做相关意义了
转移类似, 分别是跨环转移 和 环内转移
感觉这题还可以改控制最大环长, 但增大总长度, 变成矩阵乘法
代码
https://atcoder.jp/contests/abc214/submissions/33643608
1 |
|
H - Collecting
https://atcoder.jp/contests/abc214/tasks/abc214_h
有向图 N点, M边
xi个东西在点i上
k个人一个一个遍历graph
1开始, 遍历有限长度, 找最大可被收集的东西个数
限制
n2e5, m 2e5
k 10
xi [1..1e9]
4s
1024mb
题解
我的思路
显然 可以scc缩点, 然后问题变成 一个有向无环图
找k(<=10)条路径, 被经过的点的和最大
这没啥想法了
例如
a->b->c
a->b->d
a->e->d
a->e->f
一次选择的贪心 是否对全局也是最好的?
题解
果然也是先scc变成DAG
然后这里是最小费用流问题
- DAG每个点拆成out,in, 增加源S和汇T
- DAG中每个u, 增加 in[u] -> out[u], 流量1, 费用-valu, 以及无限(K)流量费用0
- DAG中(u,v)变成 out[u] -> in[v] , 流量无限(K), 费用0
- S -> in[1] 容量k, 费用0 ( 这里可以简化成去掉S, 1 作为S, 通过 in[1] -> out[1] 总容量k 来保证最大流 = K
- out[u] -> T 容量无限(k), 费用0
求min cost max flow , 答案乘上 -1
我看atcoder的库是可以限制最大流的求 mincost
然后这样做的话 代价有些是负的
办法是
- DAG中每个u, in[u] -> out[u], 流量1费用0, 流量无限(费用val[u])
- DAG中(u,v), out[u] -> in[v], 无限流量, 费用sum X[u+1..v-1]
- out[u] -> T, 无限流量, 代价sum X[u+1..N]
ans = s->t 流=K 的最小代价 - K * sum X
原理
本质上希望每个流的代价增加 sum X
那么整体形式就是 从 in[u0] -> out[u0] -> in[v1] -> out[v1] -> T
希望 每次到out[u] 的时候, 费用和的增量是 前缀和X[0..u], 这样每个 out[u] -> T, 只需要是X[0..n] - X[0..u] 即可
那么自然 out[u]-> in[v] -> out[v]
这一整段增加为 X[0..v] - X[0..u] // 保证拓扑序 来让它非负? atcoder的scc返回保证了逆向拓扑序!!
那么, 这里设计 in[v] -> out[v] 增加X[v]
所以 out[u] -> in[v] 增加 X[0..v] - X[0..u] - X[v]
它这个没有拓扑似乎也保证不了 cost非负?
保证的是没有负环!?
注意因为没有S所以 链增加的是 X[0..n] - X[0..start]
费用流 mcmf
费用流, 每个边有流量限制和每单位费用
最大流最小费 = 最短路
最大流最大费 = 最长路
满足正向单位费用的相反数 = 逆向单位费用
最小费用以流量的单价作为边权值跑最短路,注意因为不会有负环(否则费用是负无限大)所以用SPFA就可以了
如果增广路中出现了负环,那么在上一次选择中一定有一条更短的路径。(如果开始就有负环呢? 那么它说明你图建错了
最小费用流, 就是在做最大流的时候, 把dfs改成 spfa, 而距离= 路径上单位cost代价之和
代码
https://atcoder.jp/contests/abc214/submissions/33655383
1 |
|
总结
G
容斥还是不熟 感觉这个式子需要记忆 $ans = \sum_{i=0}^n (-1)^i c_i$
然后这个排列会构成多个环感觉很常用虽然知道, 但是这里通过边表示i, 分配点表示取值还是没有想到, 感觉也是一种转化思路
然后环拆成链+两头也是很经典的方法了
实现上把 环变成链 再在数组上连续性, 去做dp的方法, 比多重再算g更神奇
另外这里递推贡献更新时没有保证正确性, 有的在处理时才修复正确性 比如[1][2]
和自环
H
网络流完全不会, 学了一手费用流, 看起来就是正常最大流 变了spfa和 路径cost和
atcoder 内置的scc 和mincostflow
1 |
然后这个神奇的让 所有费用变正的 前缀和变化法 , 感觉其它地方似乎也能用