整除分块#
整除分块,用于快速求出形如
i=1∑nf(⌊in⌋)
的式子。
随着 i 的变化,⌊in⌋ 大约只有 n 种不同的取值,并且取值相同的数是连续的。也就是一段数满足:
⌊ln⌋=⌊l+1n⌋=⋯=⌊r−1n⌋=⌊rn⌋
我们想要求出这个区间的左右端点 l,r,这样 f(⌊in⌋),i∈[l,r] 就只需要算一次,再乘上 (r−l+1) 即可。
注意到端点满足:⌊ln⌋=⌊rn⌋=k∈Z,在此条件下,我们求 r 的最大值,所以有关系:
{k=⌊ln⌋r=max(i),i×k≤n
因此
r=⌊kn⌋=⌊ln⌋n
于是我们知道了,对于任意 i 满足 l≤i≤⌊⌊ln⌋n⌋,其 ⌊in⌋ 的值都是相同的。
参考资料 ↗。
求 ∑i=1n⌊in⌋.
for(int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans += (r - l + 1) * (n / l);
}
cpp
给定 n,k,求:
G(n,k)=i=1∑nkmodi
ans=i=1∑nkmodi=i=1∑nk−i×⌊ik⌋=n⋅k−i=1∑ni×⌊ik⌋
减号后面用整除分块,于是在 O(k) 的时间复杂度内解决。
01分数规划#
描述一#
每种物品有两个权值 a,b,可以从中选出其中一些,求 ∑bi∑ai 的极值。
描述二#
给定数列 ai,bi,找一组 wi∈{0,1} 使 ∑wi×bi∑wi×ai 最大,只需求出这个最大值。
二分答案,以求最大值为例。
若 mid 可行,则存在一组 wi 满足:
⇒⇒∑bi×wi∑ai×wi>mid∑ai×wi−mid∑bi×wi>0∑wi(ai−mid×bi)>0
应用见例题。
给定数列 ai,bi,找一组 wi∈0,1 使 ∑wi×bi∑wi×ai 最大,求出这个最大值。
同时要求 ∑wi×bi≥W.
W 是给定常数,n≤250,W≤1000.
需要在满足 ∑wi×bi≥W 的条件下,求出 [∑wi(ai−mid×bi)]max.
这可以用背包解决。
给定一棵 n 个节点的树,每个点有权值 ai,bi.
选出恰好 k 个点,且要求:若节点 u 被选中,则 Fatheru 也一定要被选。
求 ∑bi∑ai 的最大值,n,k≤2500.
需要在满足题目条件的前提下,求出 [∑(ai−mid×bi)]max.
考虑树上背包,设 f[u][j] 表示在 u 的子树中,取出 j 个节点的最大收益值。
求出 f 后,只需判断 f[root][k]>0 是否成立。
**转移方法:**首先有 f[u][0]=0。若 j=0,则节点 u 一定被取,分别从 u 的每棵子树 v 进行转移。即:
f[u][j]={0,k<jmax(f[u][j−k]+f[v][k]),j=0j=0
有两个点集 A,B,均包含 n 个元素。现在要让不同点集之间的元素两两配对。
对于每个配对 (Ai,Bj),会产生两个权值 ai,j 和 bi,j.
找一组配对方案,使 ∑b∑a 最大,求出这个最大值。
1≤n≤100,1≤ai,j,bi,j≤104.
先做相同的转化。这道题中,求 [∑wi(ai−mid×bi)]max 的方法是最大费用最大流。
建一个二分图,每条边流量均为 1,二分图之间连边的费用为对应的权值 w=aij−mid×bi,j.