流量守恒
对于任意非源汇点
x
x
x满足
∑
(
u
,
x
)
∈
E
f
(
u
,
x
)
=
∑
(
x
,
v
)
∈
E
f
(
x
,
v
)
\sum_{(u,x)\in E}f(u,x)=\sum_{(x,v)\in E}f(x,v)
∑(u,x)∈Ef(u,x)=∑(x,v)∈Ef(x,v).
带反向边的最大流->不带反向边的原最大流
对于边
(
u
,
v
)
(u,v)
(u,v)与其反向边
(
v
,
u
)
(v,u)
(v,u),建立新点
u
′
u'
u′,并建立边
(
u
,
u
′
)
,
(
u
′
,
v
)
,
(
v
,
u
)
(u,u'),(u',v),(v,u)
(u,u′),(u′,v),(v,u),并使得
c
(
u
,
v
)
=
c
(
u
,
u
′
)
=
c
(
u
′
,
v
)
,
c
(
v
,
u
)
=
c
(
v
,
u
)
c(u,v)=c(u,u')=c(u',v),c(v,u)=c(v,u)
c(u,v)=c(u,u′)=c(u′,v),c(v,u)=c(v,u),就是把一个二元环拆成三元环.
判定是否存在相同边的两条s到t的路径
直接把边权设为
1
1
1跑
s
s
s到
t
t
t的最大流.
判定是否存在不经过除s和t外相同点的两条s到t的路径
拆点后把点之间的流量设为1跑最大流.
最大流的反向边
c
′
(
v
,
u
)
=
f
(
u
,
v
)
c'(v,u)=f(u,v)
c′(v,u)=f(u,v).
反向边的作用就是退流(反悔).
割
一个边集使得去掉这个边集后源点到汇点的最大流为
0
0
0.
最小割
一个边集流量之和最小的割.
最小割=最大流.
费用流
在最大流的基础上用SPFA代替bfs就好了,注意反向边的费用是原边的相反数.
最大权闭合子图
给定一张有向图,有边
(
u
,
v
)
(u,v)
(u,v)表示选
u
u
u就必须选
v
v
v,每个点都有点权,现在要求选点使得点权和最大.
源点给所有正权点连边,容量为正权点的点权;所有负权点给汇点连边,费用为负权点的点权的相反数;若原图有边
(
u
,
v
)
(u,v)
(u,v)则现在的图也有边
(
u
,
v
)
(u,v)
(u,v),容量为正无穷.正权点点权和减去最小割就是答案.
注意这个只能处理DAG的情况,若有环则要依据题目对环进行处理.
CF1101D
考虑把每个点大力分解质因数,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示在以
i
i
i为根的子树经过第
i
i
i个点有
a
i
a_i
ai的第
j
j
j个质因数的最长链,可以做到
O
(
n
log
n
)
O(n\log n)
O(nlogn)解决这个问题.
CF1039D
考虑答案最多只有
O
(
n
)
O(\sqrt{n})
O(n)级别种并单调,对每种答案二分一下对应区间即可.
stirling展开
用
S
(
n
,
m
)
S(n,m)
S(n,m)表示第二类stirling数.
那么stirling展开就是:
x
k
=
∑
i
=
0
k
S
(
k
,
i
)
∗
i
!
(
x
i
)
x^k=\sum_{i=0}^{k}S(k,i)*i!\binom{x}{i}
xk=i=0∑kS(k,i)∗i!(ix)
CF1097G
将答案考虑斯特林展开:
∑
i
=
0
k
S
(
k
,
i
)
∗
i
!
∑
S
⊆
[
n
]
,
S
=
̸
∅
(
f
(
S
)
i
)
\sum_{i=0}^{k}S(k,i)*i!\sum_{S\subseteq [n],S=\not{}\empty}\binom{f(S)}{i}
i=0∑kS(k,i)∗i!S⊆[n],S≠∅∑(if(S))
考虑
∑
S
⊆
[
n
]
,
S
=
̸
∅
(
f
(
S
)
i
)
\sum_{S\subseteq[n],S=\not{}\empty}\binom{f(S)}{i}
∑S⊆[n],S≠∅(if(S))的组合意义,其实就是所有非空点集对应生成树标记了
i
i
i条边的方案数之和.
树上背包处理一下就好,时间复杂度
O
(
n
k
)
O(nk)
O(nk).
CF1061C
直接设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个数选了
j
j
j个的方案数,显然从
i
i
i到
i
+
1
i+1
i+1只修改了
a
i
+
1
\sqrt{a_{i+1}}
ai+1个位置的DP值,滚动数组优化一下就好了.
时间复杂度
O
(
n
a
i
)
O(n\sqrt{a_i})
O(nai).
CF1096D
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
s
[
1..
i
]
s[1..i]
s[1..i]中最长匹配到
h
a
r
d
hard
hard的第
j
j
j位的最小代价DP即可.
BZOJ3864
DP套DP板子题,注意到内部的DP就是一个LCS的板子.
发现每次转移只有DP值
+
0
/
1
+0/1
+0/1两种情况,状压一下就可以了.
然后预处理一下某个状态加入某个字符会转移到哪个状态,就可以进行外部DP了.
路径边权和之和
考虑每条边的贡献即可.
到所有点边权和最大的点
换根DP即可.
从序列维护到树维护.
把各种线段树
/
/
/主席树
/
/
/动态开点线段树外套个dfs序
/
/
/树上差分
/
/
/树剖.
树链剖分例题C
题意:给定两棵树,两棵树上的点权分别互不相同,每次给定两条链
(
u
1
,
v
1
)
,
(
u
2
,
v
2
)
(u_1,v_1),(u_2,v_2)
(u1,v1),(u2,v2),询问有多少点权在第一棵树
(
u
1
,
v
1
)
(u_1,v_1)
(u1,v1)上与第二棵树
(
u
2
,
v
2
)
(u_2,v_2)
(u2,v2)上都出现过.
考虑序列上的版本,离散化后把第二个序列每个位置上的值变成值在第一棵树上的对应位置,那样子就可以直接查询区间
[
u
2
,
v
2
]
[u_2,v_2]
[u2,v2]上有多少元素在
[
u
1
,
v
1
]
[u_1,v_1]
[u1,v1]中,主席树即可.
变成树上问题后,同样把第二棵树每个点点权变成点权在第一棵树上的对应位置,问题变为查询第二棵树上
(
u
2
,
v
2
)
(u_2,v_2)
(u2,v2)中有多少元素在
(
u
1
,
v
1
)
(u_1,v_1)
(u1,v1)中.
发现这时候不能直接建主席树了.考虑直接把第一棵树树剖,第二棵树对应位置变成树剖dfs序,然后就可以直接对第二棵树建主席树搞了.
时间复杂度
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n).
点分治
每次按照计算经过重心的链的信息,去掉重心后对每个连通块做相同处理.
点分治例题B
题意:求一棵树上满足点权和减去点权最大值被
p
p
p整除的路径条数.
改成模
p
p
p意义下路径信息统计,在点分治内部得到每个点到重心的点权最大值和点权和.
现在提前预处理点权和模
p
p
p意义下为
i
i
i的路径条数
c
n
t
[
i
]
cnt[i]
cnt[i],然后按照点权最大值排序并枚举更大的链统计即可.
注意下层去重.
超级钢琴
用堆维护的套路,用ST表维护区间最大值和区间最大值的位置即可.
树上路径求交
存在交当且仅当满足一条路径的lca在另一条路径上,也就是说
(
a
,
b
)
,
(
c
,
d
)
(a,b),(c,d)
(a,b),(c,d)有交当且仅当
l
c
a
(
a
,
b
)
lca(a,b)
lca(a,b)在
(
c
,
d
)
(c,d)
(c,d)上或
l
c
a
(
c
,
d
)
lca(c,d)
lca(c,d)在
(
a
,
b
)
(a,b)
(a,b)上.
一个点
x
x
x在
(
u
,
v
)
(u,v)
(u,v)上当且仅当
d
i
s
(
u
,
x
)
+
d
i
s
(
x
,
v
)
=
d
i
s
(
u
,
v
)
dis(u,x)+dis(x,v)=dis(u,v)
dis(u,x)+dis(x,v)=dis(u,v).
若在有交的时候要求交的两个端点,则需分两类:
1.
l
c
a
(
a
,
b
)
=
l
c
a
(
c
,
d
)
lca(a,b)=lca(c,d)
lca(a,b)=lca(c,d)时,则两个端点分别为
l
c
a
(
a
,
b
)
,
l
c
a
(
a
,
d
)
lca(a,b),lca(a,d)
lca(a,b),lca(a,d)中深度最大的与
l
c
a
(
b
,
c
)
,
l
c
a
(
c
,
d
)
lca(b,c),lca(c,d)
lca(b,c),lca(c,d)中深度最大的.
2.
l
c
a
(
a
,
b
)
=
̸
l
c
a
(
c
,
d
)
lca(a,b)=\not{}lca(c,d)
lca(a,b)≠lca(c,d)时,则两个端点分别为
l
c
a
(
a
,
d
)
,
l
c
a
(
b
,
c
)
lca(a,d),lca(b,c)
lca(a,d),lca(b,c)中深度最大的与
l
c
a
(
a
,
c
)
,
l
c
a
(
b
,
d
)
lca(a,c),lca(b,d)
lca(a,c),lca(b,d)中深度最大的.
树上路径维护例题A
题目:给定一棵树,要求支持给一条路径外的所有点的点权与一个常数取最大,取消某个操作,询问点
x
x
x的点权.
有一个很自然的做法,直接树剖转化为序列问题,用线段树套set维护序列,时间复杂度
O
(
m
log
3
n
)
O(m\log^3n)
O(mlog3n).
还有一个做法需要用到路径求交.考虑二分答案,并用一棵权值线段树处理权值在
[
l
,
r
]
[l,r]
[l,r]内的路径交判定即可.用RMQ求LCA可以做到
O
(
m
log
n
)
O(m\log n)
O(mlogn).
带删除背包方案数
给定一个背包,支持 m m m次操作,操作有:加入一个数,删除一个数,求容量不超过 n n n的方案数.
1 ≤ m , n ≤ 2000 1\leq m,n\leq 2000 1≤m,n≤2000.
首先我们有加入一个数的方程 f [ i ] + = f [ i − v ] f[i]+=f[i-v] f[i]+=f[i−v],反过来考虑如何删掉最后一个数,发现可以 f [ i ] − = f [ i − v ] f[i]-=f[i-v] f[i]−=f[i−v],然后发现这个方程并没有记录顺序,所以删除任意一个数都可以 f [ i ] − = f [ i − v ] f[i]-=f[i-v] f[i]−=f[i−v].
更严谨的证明可以用生成函数,设 f f f为方案数的生成函数,那么加入相当于乘上一个多项式 ( 1 + x v ) (1+x^{v}) (1+xv),删除相当于除掉一个多项式 ( 1 + x v ) (1+x^{v}) (1+xv),发现这两个操作刚好对应上面的DP方程.
限制总物品个数背包方案数
n n n种物品,第 i i i种物品重量为 i i i且个数无限,要求选不超过 m m m个物品得到重量 k k k的方案数.
1 ≤ k ≤ n ≤ 5000 1\leq k\leq n\leq 5000 1≤k≤n≤5000.
考虑设 f [ i ] [ j ] f[i][j] f[i][j]表示 i i i个物品重量为 j j j的方案数, f [ i ] [ j ] f[i][j] f[i][j]可以转移到 f [ i + 1 ] [ j + 1 ] f[i+1][j+1] f[i+1][j+1]和 f [ i ] [ j + i ] f[i][j+i] f[i][j+i],分别表示加入一个 1 1 1和把之前所有数的重量都 + 1 +1 +1.
至于为什么不会最终把一个数加到超过 n n n,是因为 k ≤ n k\leq n k≤n.
玄学背包1
给定 n n n种物品,第 i i i种物品有 i i i个,重量为 i i i,求重量之和为 m m m的方案数.
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1≤n≤105.
若 i ≤ n i\leq \sqrt{n} i≤n时,可以直接暴力做,复杂度 O ( n n ) O(n\sqrt{n}) O(nn).
若 i > n i>\sqrt{n} i>n,显然变成了一个完全背包,设 f [ i ] [ j ] f[i][j] f[i][j]表示 i i i个物品重量之和为 j j j,发现 f [ i ] [ j ] f[i][j] f[i][j]可以分别转移到 f [ i + 1 ] [ j + n + 1 ] f[i+1][j+\sqrt{n}+1] f[i+1][j+n+1]和 f [ i ] [ j + i ] f[i][j+i] f[i][j+i],由于第一维最多为 O ( n ) O(\sqrt{n}) O(n),总复杂度是 O ( n n ) O(n\sqrt{n}) O(nn)的.
合并两个背包可以直接FFT,或者直接把 i ≤ n i\leq\sqrt{n} i≤n的DP数组直接作为 i > n i>\sqrt{n} i>n的初始数组.
树的直径
树形DP入门题.
树上每个点到所有其他点的距离和
换根DP入门题.
树上每个点到其他所有点的距离最大值
把子树内和子树外分开来计算.
当然答案肯定是每个点到直径的某个端点.
树的最大独立集
树形DP入门题.
枚举子集
在状压DP中枚举 S ∈ [ 1 , n ] S\in [1,n] S∈[1,n]后枚举 T ∈ S T\in S T∈S的时间复杂度为 O ( 3 n ) O(3^n) O(3n),因为不可能有在 T T T中的元素不在 S S S中.
枚举子集代码如下:
for (int g1=0;g1<1<<n;++g1)
for (int g2=g1;g2;g2=g1&(g2-1))