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