普通分治
思想:分成两个部分分别处理,然后合并答案
朋友
一张无向图,每次询问a到b不经过c的最短路。
n<=200,Q<=1e5
Floyd,不支持删点,可用分治加点模拟删点。
复杂度
O
(
n
3
log
n
)
O(n^3\log n)
O(n3logn)
[ZJOI2016] 旅行者
给定一个网络图和若干组询问,每次询问两点间最短路。
n*m<=2e4,q<=1e5
沿着长边从中间切成两半,是起点和终点在边的两边,将边上的点跑一边 d i j k s t r a dijkstra dijkstra。
篮球
有n种物品,第i种物品有 c i c_i ci个,初始代价为ai,被选中一次代价就会减少 b i b_i bi(第 k k k次选的代价就是 a i a_i ai- ( k (k (k- 1 ) b i 1)b_i 1)bi),对于 k k k= 1 1 1… m m m,总共选 k k k件物品出来代价的最小值是多少。
n<=1000,m<=10000
一个性质,最多只有一种物品不被全选。
d
i
v
i
d
e
[
l
,
r
]
divide[l,r]
divide[l,r]表示一个
d
p
dp
dp数组,不全选的在
[
l
,
r
]
[l,r]
[l,r]中,分治。
边用
01
01
01背包维护。
复杂度
O
(
n
m
log
n
)
O(nm\log n)
O(nmlogn)
奖杯
有 n n n个数字,找到最长区间 [ l , r ] [l,r] [l,r]使得该区间每种数字出现次数都>= f [ r − l + 1 ] f[r-l+1] f[r−l+1], f f f是一个单调不增的序列。
n<=1e6
维护每种数字出现次数,然后从两端同时开始分治找到第一个不符合要求的数字。
复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
匹配
有 n n n种糖果,每种糖果有两个,但你不知道这些糖果是哪一种,有两种操作:
1.往口袋里放一颗糖果
2.从口袋里拿出一颗糖果
每次操作后你可以知道口袋里有多少种不同的糖果。输出答案将糖果两两配对。
n<=43000
不讲武德(弱化版)
给定一个二元组 ( a i , b i ) (a_i,b_i) (ai,bi),对于每个 1 1 1<= k k k<= n n n求出恰好选出 k k k个二元组使得 ( ∑ a i ) ∗ ( ( ∑ b i ) (\sum a_i)*((\sum b_i) (∑ai)∗((∑bi)最小。
n<=200
分治求凸包,找离当前左端点和右端点练出线段中间最远的点,找不到即无解。
CDQ分治
先算左侧再算左侧对右侧的贡献的分治
三维偏序
有一个三元组 ( a i , b i , c i ) (a_i,b_i,c_i) (ai,bi,ci),对于每个三元组,你要求出有多少个其他三元组 ( a j , b j , c j ) (a_j,b_j,c_j) (aj,bj,cj)满足 a j a_j aj<= a i a_i ai&& b j b_j bj<= b i b_i bi&& c j c_j cj<= c i c_i ci
n<=1e5
先按 a i a_i ai排序,分治计算自己左侧的答案,在计算对右侧的贡献。因为是按 a i a_i ai排序,所以就退化成了一个二维的偏序。按 b i b_i bi排序后树状数组维护即可。
最长偏序链
有 n n n个三元组 ( a i , b i , c i ) (a_i,b_i,c_i) (ai,bi,ci),求最多的三元组满足均存在偏序关系。
与上一题思想类似,只是把计数改为dp维护即可。
长者
一个长度为n的排序,有些位置已经确定,需要确定剩下位置上的数字使得最大化排列的最长上升子序列长度。
因为限制是一个排列,所以数字各不相同。
有一个不优的dp想法:
状态:
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示长度为
i
i
i的前缀以
j
j
j为结尾的
L
I
S
LIS
LIS长度。
转移伪代码如下:
if(/*当前位置已确定*/)
f[i][p[i]]=max(f[i-1][j]|j<p[i])+1;
else
f[i][j]=max(f[i-1][k]+1|k<j,f[i-1][j]);
然后可以考虑一下一维dp
状态:
f
[
i
]
f[i]
f[i]表示以
i
i
i位置结尾的最长
L
I
S
LIS
LIS且
i
i
i位置已确定。
转移:枚举已确定的位置,
f
[
i
]
=
m
a
x
(
f
[
j
]
+
m
i
n
(
c
n
t
[
i
]
−
c
n
t
[
j
]
,
r
e
m
[
p
[
i
]
]
−
r
e
m
[
p
[
i
]
]
)
∣
p
[
j
]
<
p
[
i
]
)
+
1
f[i]=max(f[j]+min(cnt[i]-cnt[j],rem[p[i]]-rem[p[i]])|p[j]<p[i])+1
f[i]=max(f[j]+min(cnt[i]−cnt[j],rem[p[i]]−rem[p[i]])∣p[j]<p[i])+1
c
n
t
cnt
cnt表示前缀中没有确定的数字个数
r
e
m
rem
rem表示<
p
[
i
]
p[i]
p[i]且未确定的数字个数
因为满足
p
[
j
]
p[j]
p[j]<
p
[
i
]
p[i]
p[i]且
j
j
j<
i
i
i,所以min可分情况讨论。
- c n t [ i ] cnt[i] cnt[i]- c n t [ j ] cnt[j] cnt[j]>= r e m [ p [ i ] ] rem[p[i]] rem[p[i]]- r e m [ p [ i ] ] rem[p[i]] rem[p[i]] => c n t [ i ] cnt[i] cnt[i]- r e m [ p [ i ] ] rem[p[i]] rem[p[i]]>= c n t [ j ] cnt[j] cnt[j]- r e m [ p [ j ] ] rem[p[j]] rem[p[j]]
- c n t [ i ] cnt[i] cnt[i]- c n t [ j ] cnt[j] cnt[j] < r e m [ p [ i ] ] rem[p[i]] rem[p[i]]- r e m [ p [ i ] ] rem[p[i]] rem[p[i]] => c n t [ i ] cnt[i] cnt[i]- r e m [ p [ i ] ] rem[p[i]] rem[p[i]] < c n t [ j ] cnt[j] cnt[j]- r e m [ p [ j ] ] rem[p[j]] rem[p[j]]
所以可以分治下标,按 c n t [ i ] cnt[i] cnt[i]- c n t [ j ] cnt[j] cnt[j]排序。
HDU 6800
一个二维点序列 ( a i , b i ) (a_i,b_i) (ai,bi),把它拆成两个子序使得两个子序列中相邻两项的曼哈顿距离和最小。
状态:
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示当前
d
p
dp
dp了前
i
i
i个,另一个子序列结尾为
j
j
j的最小代价。
转移伪代码如下:
if(j<i-1)
f[i][j]->f[i-1][j]
else //枚举k
f[i][j]=f[i-1][k]
所以我们可以设
g
[
i
]
g[i]
g[i]=
f
[
i
,
i
f[i,i
f[i,i-
1
]
1]
1]
则
g
[
i
]
g[i]
g[i]=
m
i
n
(
g
[
j
]
min(g[j]
min(g[j]+
d
[
j
−
1
]
[
i
]
d[j-1][i]
d[j−1][i]-
s
[
j
]
)
s[j])
s[j])+
s
[
i
−
1
]
s[i-1]
s[i−1]
然后分治就可做了。
线段树分治
序列
有 n n n个物品,第 i i i种物品第一次选择的收益是 a i a_i ai,之后每一次选择的收益都是 b i b_i bi,代价始终为 c i c_i ci,在总代价不超过 m m m的情况下最大化总收益。
有 Q Q Q次询问,每次修改一个物品的两类收益,对于每次修改输出答案(之前的修改对后面有影响)。
n,m,Q<=2000
先考虑没有修改的情况:
强制选一个
a
i
a_i
ai,转移到
f
[
i
]
[
j
f[i][j
f[i][j+
c
i
]
c_i]
ci],
f
[
i
]
[
j
]
f[i][j]
f[i][j]=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
]
[
j
max(f[i][j],f[i][j
max(f[i][j],f[i][j-
c
[
i
]
c[i]
c[i]+
b
[
i
]
)
b[i])
b[i]),与
f
[
i
f[i
f[i-
1
]
[
j
]
1][j]
1][j]取
m
a
x
max
max
若存在修改,它的贡献为一段连续的
d
f
s
dfs
dfs序
所以每个物品可能会被修改多次,所以可以把每个区间放入线段树中,线段树分治维护。
无题
有 n n n个物品,每个物品有大小和权值,查询一个区间中的物品拿出来做大小为 w w w的背包的结果
n,m<=1e4,w<=100
线段树分治。
考虑n,m<=1e5,可用双栈 维护
luogu P5163
老师高一出的黑题 我是垃圾
维护一个动态有向图,支持三种操作:
1.删掉一条有向边
2.改变一个点的权值
3.询问一个点所在的强连通分量里前 k k k大的权值之和
n,m<=2e5 允许离线
首先删边可以反过来加边。
可以整体二分 来计算出每条边被缩。
二分一个时间mid,把出现时间<=mid的边跑一边tarjan,如果当前边被缩过了,就递归到左边,否则递归右边,递归到底时用并查集 缩边。就把时间全部求出,然后按时间排序就变成了无向图。
所以我们可以用线段树合并,查询线段树二分即可。
二进制分组
有1000桶水,其中有一桶有毒,你可以要来若干只小白鼠让它们喝桶里的水,一只小白鼠可以喝多个桶里的水,毒性会在一天后发作。
你需要在一天内找出有毒的那桶水,至少需要多少只小白鼠?
CF1365G
传送门我真不想敲题面了
首先显然有20次的二进制分组做法, 但是题目要求13次,还是多了,所以要想办法优化
以发现
C
13
6
C_{13}^6
C136>1000,这就代表着我们对这1000个数用一个13位的有6个1的二进制标记, 每一个的二进制都是不同的, 而且绝对没有包含关系, 这样的话, 我们就可以去求出每一个位置上的权值