文章目录
牛客1
A
1.性质
求出每个后缀对应的b数组,会发现:后缀的b数组,就是在原字符串的b数组的基础上,把一些值改为0。
2.分类讨论
进一步分析发现b数组中,最多存在两个0,所以只要根据0的位置,来判断两个b数组的大小,最后通过改写比较级进行排序就可以了。
3.后缀数组
分类讨论时发现要比较两个后缀的大小,这时就可以套用后缀数组的模板,得到排名,来比较大小。
H
1.判断是否合法
先计算容量为1时的最大流m,当容量变为
u
/
v
u/v
u/v时,最大流变成
m
∗
u
/
v
m*u/v
m∗u/v(容量和最大流成比例)。接着判断
m
∗
u
/
v
m*u/v
m∗u/v是否大于等于1。
2.按比例计算费用流
跑一次增广路时,增加1个单位(以边容量为1个单位)的费用,所以把:跑每一次增广路时的费用记录下来。接着把流量1按照边容量
u
/
v
u/v
u/v为基本单位,分配费用。为了方便计算,就变成了:把流量v按照边容量u为基本单位,分配费用,结果再除以v。
I
1.转化为匹配问题。
用“度拆点”,“边拆点”的技巧建图,转化为匹配问题。
2.建图
“度拆点”:把同一个点的多个度数拆成多个点
“边拆点”:把边的两个端点拆成两个点
“连线”:度数的点连接对应的边的端点,两个边的端点互相连接。
3.跑一般图匹配
建好图后,用带花树算法得到匹配。如果是完美匹配(所有点都被匹配到),就说明存在,否则不存在。
4.匹配边的意义
如果一条匹配边,一个端点是度数的点,另一个端点是边的端点,代表着边对应的点被选上了。如果一条匹配边,两边都是边的端点,代表着边对应的点没被选上,删去这些没被选上的边,就可以得到答案了。
牛客2
A
1.枚举+桶
考虑枚举所有字符串的前缀,然后找有多少个字符串的后缀与这个前缀相同。解决方法:计算出每个字符串的后缀hash值,然后存到桶里面。
2.去重
上述方法会重复计算一些情况。原因是:在枚举一个字符串的前缀时,可能会有不同前后缀,但是匹配到同一个字符串的问题。例如当前字符串为:aba???,因为a既是aba的前缀,也是aba的后缀,所以在枚举前缀时会算重复。解决方法:用kmp的next数组去重。
B
1.性质
找任意两点的中垂线的交点,相同交点数最多的就是答案
2.优化
因为用的是map,所以map不能装太多点(小于1e6),否则容易超时。所以,在枚举一个点,先记录下与其它的点的中垂线交点,放到map里面,然后算出答案,清空map后,再枚举下一个点。
H
1.分类讨论
可以把
M
S
MS
MS看成是一个排好序的序列,首先知道:三角形判断的充要条件是:最小两边和大于第三边,然后对
a
,
b
,
x
a,b,x
a,b,x的大小进行分类讨论(假设
a
≥
b
a \ge b
a≥b):
(1) b ≤ a ≤ x b \le a \le x b≤a≤x:这时直接找离 x x x最近的两个数,即 x x x的两个前驱,这两个前驱就是 a , b a,b a,b,然后判断 a + b a+b a+b是否大于 x x x即可。
(2) b ≤ x < a b \le x < a b≤x<a:根据大小关系,我们要找的 a , b a,b a,b应该满足: b + x > a b+x>a b+x>a,即: x > a − b x>a-b x>a−b,意思就是要使 a − b a-b a−b尽量小。又因为 a , b a,b a,b在 x x x的两边( b ≤ x b \le x b≤x, x ≤ a x \le a x≤a),所以选 x x x的前驱作为 b b b, x x x的后驱作为 a a a是最优的。接着判断是否符合三角形的要求就可以了。
(3) x < b ≤ a x < b \le a x<b≤a:同(2),要找满足: x > a − b x>a-b x>a−b 的 a , b a,b a,b。即查找 M S MS MS中,大于 x x x的数的部分的最小差。
2.求 1(3) 中的最小差
用数据结构优化的思路是:把要维护的东西看成是有序的序列,然后才用数据结构,去维护这个有序的序列。
(1)平衡树(题解做法)
平衡树的性质:左子树所有的节点小于当前节点,右子树所有的节点大于当前节点。
平衡树每个节点维护的内容:当前节点与前驱的最小差
d
t
dt
dt,子树中所有节点
d
t
dt
dt的最小值。
插入和删除的时候并不难,在回溯时就可以更新维护的内容。查询的时候,首先找到 x x x的后驱在平衡树的位置,叫节点 p p p。因为要找的是:大于 x x x的部分的最小差,所以,我们可以从 p p p开始,找 p p p的右子树(平衡树性质)的最小 d t dt dt值。因为大于 x x x的数不单只有 p p p的右子树部分,所以 p p p还要往根节点的方向走,统计合法的 d t dt dt值,具体自行画一下图(牢记平衡树性质)。
(2)权值线段树(自己的做法)
权值线段树维护的内容:当前区间(数值范围)数的数量,最大值,最小值,最小差(类似查询全局最小差,我是参考这里的)。问题由全局最小差,变成了区间最小差。
区间最小差要注意的地方:因为询问一个区间时,会被线段树拆成很多个区间,但是线段树并没有记录这些拆的区间,之间的最小差,所以要手动解决。我的方法:把线段树拆成的区间中的最大值和最小值加入到一个 n u m num num数组,排序去重后直接 O ( s i z e ( n u m ) ) O(size(num)) O(size(num))算最小差。因为线段树拆出来的区间不多,所以这样的做法还是挺快的。注意:不要把不存在的最大值和最小值加入到 n u m num num。
牛客3
D
1.性质
对于每行(每列)来说,从左到右,一定是:一堆白点->一堆黑点->一堆白点相间,而且起始和末尾都是白点,所以对数一定是偶数(一堆黑点和左边白点会产生一对,一堆黑点和右边白点又会产生一对)。
2.判断是否合法
由1的性质可知m必须是偶数,否则不合法。
m的最大值
m
m
a
x
mmax
mmax:
4
∗
n
4*n
4∗n,因为一个黑点最多和上下左右四个白点组成一对。
m的最小值
m
m
i
n
mmin
mmin:因为需要匹配的对数少,所以考虑把黑点放在一起,所有黑点都在a行b列内,且按顺序(从左到右,从下到上)摆放。a和b需要满足的条件:
a
∗
b
≥
n
a*b \ge n
a∗b≥n,
2
(
a
+
b
)
=
m
m
i
n
2(a+b)=mmin
2(a+b)=mmin。通过这个条件,我们使
2
(
a
+
b
)
2(a+b)
2(a+b)尽量小,就可以求出m的最小值了。
3.构造合法的解
在算m的最小值时,就已经构造出了解的一部分。如果现在的m是最小的,那么就已经得到了答案。如果现在的m不是最小的,就从最后一个黑点开始,把这个点扔到很远的地方。如果这个点是一行(列)中最后一个点,那么可以增加:
4
−
2
=
2
4-2=2
4−2=2个对(由1的性质可知),否则增加
4
4
4个对。重复这个操作,直到对数大于等于m。如果对数大于m,这时对数肯定只比m大
2
2
2,则把刚刚扔出去的其中一个黑点,与其他任意一个黑点相邻,使对数减
2
2
2,就得到答案了。
E
1.题目本质
由于题目说
p
p
i
=
i
p_{p_i}=i
ppi=i,所以有:
a
p
i
−
a
p
p
i
=
a
p
i
−
a
i
a_{p_i}-a_{p_{p_i}}=a_{p_i}-a_i
api−appi=api−ai。所以就是找数组a的两个数相匹配,所以题目变为:给出一个a数组,找到两种不同的匹配方法,使这两种匹配方法得到的和最小。
2.性质
匹配时,会发现连续4个,或者连续6个为一组进行匹配,情况是最优的。
3.dp
对于当前位置,是选择连续4个,还是连续6个,可以用dp解决。
F
1.
g
c
d
(
a
,
b
)
>
1
gcd(a,b)>1
gcd(a,b)>1
随便选一个大于1的公因子
g
g
g,则:
a
/
b
=
(
x
g
)
/
(
y
g
)
=
(
x
+
1
)
g
/
y
g
−
g
/
y
g
=
(
x
+
1
)
/
y
−
1
/
y
a/b=(xg)/(yg)=(x+1)g/yg-g/yg=(x+1)/y-1/y
a/b=(xg)/(yg)=(x+1)g/yg−g/yg=(x+1)/y−1/y接着对应套上去就可以了。
2.
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1且
b
b
b的质因子个数大于1
通分一下题目的公式,得:
(
c
f
−
e
d
)
/
(
d
f
)
=
a
/
b
(cf-ed)/(df)=a/b
(cf−ed)/(df)=a/b令:
d
f
=
b
df=b
df=b,然后求出符合条件的
d
,
f
d, f
d,f:
g
c
d
(
d
,
f
)
=
1
gcd(d,f)=1
gcd(d,f)=1
接着,令:
c
f
−
e
d
=
a
cf-ed=a
cf−ed=a,因为
g
c
d
(
d
,
f
)
=
1
gcd(d,f)=1
gcd(d,f)=1,所以此方程必定有解。接着就用扩展欧几里得求出
c
,
e
c, e
c,e就可以了。要注意的是:扩欧求出来的
c
,
e
c, e
c,e不一定大于0,所以要记得转化一下。
3.
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1且
b
b
b的质因子个数为1
无解。证明:设
b
=
p
k
b=p^k
b=pk,
p
p
p为质数,则:
d
=
x
p
k
1
d=xp^{k_1}
d=xpk1,
f
=
y
p
k
2
f=yp^{k_2}
f=ypk2,所以:
c
d
−
e
f
=
c
x
p
k
1
−
e
y
p
k
2
=
c
p
k
−
k
1
−
e
p
k
−
k
2
x
y
p
k
\frac{c}{d}-\frac{e}{f}=\frac{c}{xp^{k_1}}-\frac{e}{yp^{k_2}}=\frac{cp^{k-k_1}-ep^{k-k_2}}{xyp^k}
dc−fe=xpk1c−ypk2e=xypkcpk−k1−epk−k2因为:
k
1
,
k
2
<
k
k1,k2<k
k1,k2<k,所以:
k
−
k
1
>
0
k-k_1>0
k−k1>0,
k
−
k
2
>
0
k-k_2>0
k−k2>0
即:下面的
p
k
p^k
pk还可以被约,但是因为
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1,所以分母必须保留
p
k
p^k
pk这个因子,这与
p
k
p^k
pk被约矛盾,所以无解。
牛客6
H
1.数位dp
记忆化搜索中,数位dp的必备状态:搜索的数位位置
p
o
s
pos
pos,搜索位的限制
l
i
m
i
t
limit
limit。这道题的数位dp的状态:搜索的数位位置
p
o
s
pos
pos,搜索到当前位的
A
A
A与
B
B
B的数位和的差
d
e
l
t
a
delta
delta。又因为
A
≤
B
A \le B
A≤B,所以还要有两个
l
i
m
i
t
limit
limit变量:
l
i
m
i
t
A
limitA
limitA和
l
i
m
i
t
B
limitB
limitB。接下来,只需要在当前位
p
o
s
pos
pos,根据
l
i
m
i
t
A
limitA
limitA和
l
i
m
i
t
B
limitB
limitB,枚举
A
A
A和
B
B
B的数位,剩下的套数位dp模板就可以了。转移方程(建议先自行思考):
p
o
s
>
0
:
pos > 0:
pos>0:
d
f
s
(
p
o
s
,
d
e
l
t
a
,
l
i
m
i
t
B
,
l
i
m
i
t
A
)
dfs(pos, delta, limitB,limitA)
dfs(pos,delta,limitB,limitA)
=
∑
0
≤
i
≤
u
p
B
∑
0
≤
j
≤
u
p
A
d
f
s
(
p
o
s
−
1
,
d
e
l
t
a
+
j
−
i
,
l
i
m
B
,
l
i
m
A
)
,
=\sum\limits_{0 \le i \le upB}\sum\limits_{0 \le j \le upA} dfs(pos-1,delta+j-i,limB,limA),
=0≤i≤upB∑0≤j≤upA∑dfs(pos−1,delta+j−i,limB,limA),
u
p
B
=
{
n
u
m
[
p
o
s
]
limitB = 1
9
limitB = 0
,
upB= \begin{cases} num[pos]& \text{limitB = 1}\\ 9& \text{limitB = 0} \end{cases},
upB={num[pos]9limitB = 1limitB = 0,
u
p
A
=
{
i
limitA = 1
9
limitA = 0
,
upA= \begin{cases} i& \text{limitA = 1}\\ 9& \text{limitA = 0} \end{cases},
upA={i9limitA = 1limitA = 0,
l
i
m
B
=
{
1
i = upB and limitB = 1
0
other
,
limB= \begin{cases} 1& \text{i = upB and limitB = 1}\\ 0& \text{other} \end{cases},
limB={10i = upB and limitB = 1other,
l
i
m
A
=
{
1
j = upA and limitA = 1
0
other
limA= \begin{cases} 1& \text{j = upA and limitA = 1}\\ 0& \text{other} \end{cases}
limA={10j = upA and limitA = 1other
p
o
s
=
0
:
pos = 0:
pos=0:
d
f
s
(
p
o
s
,
d
e
l
t
a
,
l
i
m
i
t
B
,
l
i
m
i
t
A
)
=
{
1
delta > 0
0
other
dfs(pos, delta, limitB,limitA)= \begin{cases} 1& \text{delta > 0}\\ 0& \text{other} \end{cases}
dfs(pos,delta,limitB,limitA)={10delta > 0other
2.细节
(1)存数位和的差时,小于0的差一定也要记录,这样才能覆盖整个解空间
(2)有些数位dp模板可能没有记忆化
l
i
m
i
t
limit
limit变量。在这道题中,
l
i
m
i
t
A
limitA
limitA和
l
i
m
i
t
B
limitB
limitB也要记忆化,不然会超时
(3)dp数组一定要初始化为-1
牛客9
J
1.查询矩阵和
考虑用二维前缀和,
s
u
m
[
n
]
[
m
]
sum[n][m]
sum[n][m]代表
∑
1
≤
i
≤
n
,
1
≤
j
≤
m
a
i
,
j
\sum\limits_{1\le i \le n, 1 \le j \le m} {a_{i,j}}
1≤i≤n,1≤j≤m∑ai,j,预处理后就可以通过
s
u
m
sum
sum得到任意一个子矩阵的和。在这之前,先要把矩阵
a
a
a中的
0
0
0,修改成
−
1
-1
−1。目的是:能让后面用“桶”统计答案。
2.枚举矩阵
普通枚举矩阵的方法:枚举矩阵的上下行,左右列。在这里因为要更快统计答案,只枚举了矩阵的上下行,右列。
3.统计答案
假设已经枚举了矩阵的上行
U
U
U,下行
D
D
D。接着考虑前缀和维护:
s
u
m
2
[
R
]
sum2[R]
sum2[R]代表
∑
U
+
1
≤
i
≤
D
−
1
,
1
≤
j
≤
R
a
i
,
j
\sum\limits_{U+1\le i \le D-1, 1 \le j \le R} {a_{i,j}}
U+1≤i≤D−1,1≤j≤R∑ai,j,这样就可以通过
s
u
m
2
sum2
sum2的前缀和相减,得到子矩阵和。因为题目要求子矩阵内
0
0
0和
1
1
1的数量差值不超过
1
1
1,所以子矩阵内的和在
[
−
1
,
1
]
[-1,1]
[−1,1]内。即可以转化为:如果
s
u
m
2
sum2
sum2中存在两个值的差值不超过
1
1
1,那么这两个值可以贡献一个答案。
要统计有多少对值的差值不超过
1
1
1,可以考虑在连续为
1
1
1的上下行中,枚举
s
u
m
2
sum2
sum2中的值(右列),把满足列全为1的
s
u
m
2
sum2
sum2值(左列)放进桶
c
n
t
cnt
cnt里面,询问
s
u
m
2
sum2
sum2的值加
1
1
1、加
0
0
0、减
1
1
1的值的数量。
4.桶的处理
如果用map/unordered_map,很可能会超时。因为考虑到前缀和的范围:
[
−
500
∗
500
,
500
∗
500
]
[-500*500, 500*500]
[−500∗500,500∗500],所以我们可以直接开一个数组来用作桶,偏移一下范围。一个数加入桶时,记录这个数,最后通过这些记录的数清空桶。
5.边界处理
在计算矩阵和时,要记得矩阵边界是不包括的。
牛客10
J
1.递归解决子问题
如果要匹配节点
t
1
,
t
2
t_1,t_2
t1,t2对应的树,可以考虑:
t
1
t_1
t1中的儿子节点对应的子树,与
t
2
t_2
t2中的儿子节点对应的子树,进行匹配。
2.解决匹配问题
假设已经计算出(通过递归计算):
t
1
t_1
t1的子树
s
o
n
1
son_1
son1,与
t
2
t_2
t2的子树
s
o
n
2
son_2
son2进行匹配需要的花费为
c
o
s
t
cost
cost,那么可以建一条
s
o
n
1
son_1
son1和
s
o
n
2
son_2
son2之间的边,边权为
c
o
s
t
cost
cost。如果选中(匹配到)这条边,代表子树
s
o
n
1
son_1
son1与子树
s
o
n
2
son_2
son2进行匹配,花费为
c
o
s
t
cost
cost。然后,我们对所有的
s
o
n
1
son_1
son1,与所有的
s
o
n
2
son_2
son2都建立边,形成图
G
G
G。接着,对这个图
G
G
G跑二分图最小权匹配,就可以得到最优的匹配方案了。
3.二分图最小权匹配
可以考虑:KM算法、费用流
4.细节
(1)树的大小要相同才能匹配
(2)树
t
1
t_1
t1和树
t
2
t_2
t2匹配的花费,除了子树匹配的花费(图
G
G
G)外,还有根节点
t
1
t_1
t1与
t
2
t_2
t2进行匹配的花费。
杭电1
1005 (hdu 6755)
1.斐波那契数列通项公式
F
n
=
1
5
[
(
1
+
5
2
)
n
−
(
1
−
5
2
)
n
]
F_n=\frac{1}{\sqrt{5}} [ (\frac{1+\sqrt{5}}{2})^n- (\frac{1-\sqrt{5}}{2})^n]
Fn=51[(21+5)n−(21−5)n]2.
5
\sqrt{5}
5在模
1
e
9
+
9
1e9+9
1e9+9意义下的整数
因为:
38300801
6
2
≡
61699199
3
2
≡
5
(
m
o
d
1
e
9
+
9
)
383008016^2≡616991993^2≡5 \ (mod \ 1e9+9)
3830080162≡6169919932≡5 (mod 1e9+9)
所以:
5
≡
383008016
(
m
o
d
1
e
9
+
9
)
\sqrt{5}≡383008016 \ (mod \ 1e9+9)
5≡383008016 (mod 1e9+9)
或
5
≡
616991993
(
m
o
d
1
e
9
+
9
)
\sqrt{5}≡616991993 \ (mod \ 1e9+9)
5≡616991993 (mod 1e9+9)。
计算时选其中一个就可以了,无论选哪个都不影响最后的结果。
3.化简题目式子
设:
a
=
1
+
5
2
a=\frac{1+\sqrt{5}}{2}
a=21+5,
b
=
1
−
5
2
b=\frac{1-\sqrt{5}}{2}
b=21−5,
c
=
1
5
c=\frac{1}{\sqrt{5}}
c=51,因此:
F
n
=
d
(
a
n
−
b
n
)
F_n=d(a^n-b^n)
Fn=d(an−bn)
所以:
∑
i
=
0
N
(
F
C
i
)
K
=
∑
i
=
0
N
[
d
(
a
C
i
−
b
C
i
)
]
K
=
d
K
∑
i
=
0
N
(
a
C
i
−
b
C
i
)
K
\sum\limits_{i=0}^{N} (F_{Ci})^K=\sum\limits_{i=0}^{N}[d(a^{Ci}-b^{Ci})]^K=d^K\sum\limits_{i=0}^{N} (a^{Ci}-b^{Ci})^K
i=0∑N(FCi)K=i=0∑N[d(aCi−bCi)]K=dKi=0∑N(aCi−bCi)K接着,我们对
(
a
C
i
−
b
C
i
)
K
(a^{Ci}-b^{Ci})^K
(aCi−bCi)K进行二项式展开,得到其中一项是:
(
−
1
)
m
C
K
m
(
a
C
i
)
K
−
m
(
b
C
i
)
m
=
(
−
1
)
m
C
K
m
(
a
C
(
K
−
m
)
)
i
(
b
C
m
)
i
=
(
−
1
)
m
C
K
m
(
a
C
(
K
−
m
)
b
C
m
)
i
(-1)^mC^{m}_{K} (a^{Ci})^{K-m}(b^{Ci})^{m}=(-1)^mC^{m}_{K} (a^{C(K-m)})^i(b^{Cm})^i=(-1)^mC^{m}_{K} (a^{C(K-m)} b^{Cm})^i
(−1)mCKm(aCi)K−m(bCi)m=(−1)mCKm(aC(K−m))i(bCm)i=(−1)mCKm(aC(K−m)bCm)i令
q
m
=
a
C
(
K
−
m
)
b
C
m
q_m=a^{C(K-m)} b^{Cm}
qm=aC(K−m)bCm,得:
∑
i
=
0
N
(
−
1
)
m
C
K
m
(
a
C
(
K
−
m
)
b
C
m
)
i
=
(
−
1
)
m
C
K
m
∑
i
=
0
N
q
m
i
=
(
−
1
)
m
C
K
m
q
m
N
+
1
−
1
q
m
−
1
\sum\limits_{i=0}^{N} (-1)^mC^{m}_{K} (a^{C(K-m)} b^{Cm})^i=(-1)^mC^{m}_{K} \sum\limits_{i=0}^{N} q_m^i=(-1)^mC^{m}_{K} \frac{q^{N+1}_m-1}{q_m-1}
i=0∑N(−1)mCKm(aC(K−m)bCm)i=(−1)mCKmi=0∑Nqmi=(−1)mCKmqm−1qmN+1−1所以,答案就是把二项式的全部项加起来,即:
a
n
s
=
∑
m
=
0
K
(
−
1
)
m
C
K
m
q
m
N
+
1
−
1
q
m
−
1
ans=\sum\limits_{m=0}^{K} (-1)^mC^{m}_{K} \frac{q_m^{N+1}-1}{q_m-1}
ans=m=0∑K(−1)mCKmqm−1qmN+1−1时间复杂度为:
O
(
K
l
o
g
N
)
O(KlogN)
O(KlogN)
4.计算优化
如果上述所有数都用快速幂求,大概率会超时。所以要尽可能少用快速幂,用递推优化。能递推优化计算的有:
q
m
q_m
qm,
q
m
N
+
1
q_m^{N+1}
qmN+1。
q
m
=
q
m
−
1
∗
b
c
/
a
−
c
q_m=q_{m-1}*b^c/a^{-c}
qm=qm−1∗bc/a−c,
q
m
N
+
1
=
q
m
−
1
N
+
1
∗
b
c
(
N
+
1
)
/
a
−
c
(
N
+
1
)
q^{N+1}_m=q^{N+1}_{m-1}*b^{c(N+1)}/a^{-c(N+1)}
qmN+1=qm−1N+1∗bc(N+1)/a−c(N+1)
其中,
b
c
,
b
c
(
N
+
1
)
b^c, b^{c(N+1)}
bc,bc(N+1)可以先算出来;
a
−
c
a^{-c}
a−c的逆元,
a
−
c
(
N
+
1
)
a^{-c(N+1)}
a−c(N+1)的逆元,也可以提前算出来。接着就可以用
O
(
K
)
O(K)
O(K)的递推算出上述两个数。
1006 (hdu 6756)
1.性质
(1)图中度数超过
m
\sqrt{m}
m的点的数量小于等于
m
\sqrt{m}
m。
(2)一个点的
M
E
X
MEX
MEX的最大值是这个点的度数,所以大于等于度数的
A
A
A值都是可以忽略的。
2.求
M
E
X
MEX
MEX
对每个点都建立一个存值的“桶”数组
c
n
t
cnt
cnt,数组大小不超过这个点的度数。然后,再建立一个标记数组
v
i
s
vis
vis(标记数组不用真实创建,因为标记数组要用数据结构维护,这里只是为了方便叙述),大小与“桶数组”相等,
v
i
s
[
i
]
=
1
vis[i]=1
vis[i]=1代表存在这个值,即
c
n
t
[
i
]
≥
1
cnt[i] \ge 1
cnt[i]≥1,
v
i
s
[
i
]
=
0
vis[i]=0
vis[i]=0则相反。
(1)树状数组
用树状数组维护
v
i
s
vis
vis数组的前缀和,记为
p
r
e
pre
pre。假设
M
E
X
=
x
MEX=x
MEX=x,则有:
p
r
e
i
=
i
+
1
(
i
<
x
)
pre_i=i+1\ (i < x)
prei=i+1 (i<x),
p
r
e
i
≠
i
+
1
(
i
≥
x
)
pre_i \ne i+1\ (i \ge x)
prei=i+1 (i≥x),所以可以利用这个性质,对
M
E
X
MEX
MEX进行二分,然后通过查询前缀和来
c
h
e
c
k
check
check答案,时间复杂度为:
O
(
l
o
g
n
l
o
g
n
)
O(logn \ logn)
O(logn logn)。
(2)分块
对
v
i
s
vis
vis数组进行分块,维护一个块内的和,块的大小为:
u
n
i
t
=
n
unit = \sqrt{n}
unit=n。计算
M
E
X
MEX
MEX时,先从前往后扫描一个块,直到这个块的和不等于块的大小。这时,就找到这个块对应的
c
n
t
cnt
cnt数组的区域,从前往后扫描,直到
c
n
t
i
=
0
cnt_i=0
cnti=0,时间复杂度:
O
(
2
n
)
O(2\sqrt{n})
O(2n)
3.修改与查询
由1的性质,我们把度数小于等于
m
\sqrt{m}
m的点称作:“小点”,把度数大于
m
\sqrt{m}
m的点称作:“大点”。对于 “大点” ,需要额外维护 “旧值” 。
(1)对于修改操作:
对于小点,因为度数小于等于
m
\sqrt{m}
m,所以暴力修改所有邻接点的
v
i
s
vis
vis数组和
c
n
t
cnt
cnt数组,然后修改自身的值。对于大点,则直接修改自身的值(注意不要修改 “旧值” )。
(2)对于查询操作:
对于所有点(无论是大点还是小点),思路是:先遍历相邻的 “大点” ,利用 “大点” 的值和 “旧值” ,修改自身的
v
i
s
vis
vis数组和
c
n
t
cnt
cnt数组。接着,利用修改完后的
v
i
s
vis
vis数组和
c
n
t
cnt
cnt数组进行查询。最后,再次遍历相邻的 “大点” ,把自身的
v
i
s
vis
vis数组和
c
n
t
cnt
cnt数组还原回去。
对于上述算法的时间复杂度,如果 v i s vis vis数组是用树状数组维护,则为 O ( q m l o g n ) O(q\sqrt{m}\ logn) O(qm logn);如果 v i s vis vis数组用的是分块维护,则为 O ( q ( m + n ) ) O(q(\sqrt{m}+\sqrt{n})) O(q(m+n))。实测两种方法对于 o j oj oj上的数据似乎差别不大。
杭电2
1012 (hdu 6774)
1.性质
两个字符串的距离:两个字符串的长度 - 2 * 两个字符串的最长公共子序列。
2.预处理
对
A
A
A进行预处理,预处理出数组
f
[
i
]
[
c
h
]
f[i][ch]
f[i][ch]:在
A
[
i
]
A[i]
A[i]后第一个出现字符
c
h
ch
ch的位置。
3.求最长公共子序列(
L
C
S
LCS
LCS)
(1)
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]的意义:
下标
i
i
i:
B
B
B的前缀:
B
[
1...
i
]
B[1...i]
B[1...i]
下标
j
j
j:
B
[
1...
i
]
B[1...i]
B[1...i]与
A
[
l
.
.
.
r
]
A[l...r]
A[l...r]的
L
C
S
LCS
LCS长度
d
p
dp
dp值:令
L
C
S
LCS
LCS成立的
A
[
l
.
.
.
r
]
A[l...r]
A[l...r]的最短前缀长度
(2)转移方程:
d
p
[
i
]
[
j
]
=
m
i
n
{
d
p
[
i
−
1
]
[
j
]
don’t select B[i]
f
[
l
+
d
p
[
i
−
1
]
[
j
−
1
]
]
[
B
[
i
]
]
−
l
+
1
select B[i]
dp[i][j]=min \begin{cases} dp[i-1][j]& \text{don't select B[i]}\\ f[l+dp[i-1][j-1]][B[i]]-l+1& \text{select B[i]} \end{cases}
dp[i][j]=min{dp[i−1][j]f[l+dp[i−1][j−1]][B[i]]−l+1don’t select B[i]select B[i]最后注意一下限制条件和初始化就可以了。
杭电5
1007 (hdu 6820)
1.连通图
如何“构成”连通图:在一个有根树中,一个连通图,可以从一个子树的根节点出发,然后一直连通到子树中的其他节点。所以,可以考虑统计子树内贡献的dp解决。
2.树型dp
(1)先考虑子树的根节点,与父节点相连,构成连通图的情况:
设
x
x
x为子树的根节点,则:
- d p [ x ] [ 0 ] dp[x][0] dp[x][0]代表子树中,所有节点的儿子数量都小于等于 k − 1 k-1 k−1(即所有节点度数小于等于 k k k)的子连通图最大边权和。
- d p [ x ] [ 1 ] dp[x][1] dp[x][1]代表子树中,已经有1个节点的儿子数量大于 k − 1 k-1 k−1(即已经有1个节点的度数大于 k k k)的子连通图最大边权和。
(自己的理解:因为连通图拆成子连通图,就只有两种状态:一是图中节点度数小于等于 k k k,另外是图中有 1 1 1个节点度数大于 k k k)
(2)状态转移:
对于
d
p
[
x
]
[
0
]
dp[x][0]
dp[x][0],只需要取
x
x
x的儿子中的前
k
−
1
k-1
k−1个最大
d
p
[
0
]
dp[0]
dp[0]值就可以了。
对于
d
p
[
x
]
[
1
]
dp[x][1]
dp[x][1],有两种情况:
- 子树中,根节点 x x x的儿子数量大于 k − 1 k-1 k−1:直接选取所有儿子的 d p [ 0 ] dp[0] dp[0]值
- 子树中,非根节点的儿子数量大于 k − 1 k-1 k−1:需要选取 x x x的 k − 2 k-2 k−2个儿子的 d p [ 0 ] dp[0] dp[0]值,和1个儿子的 d p [ 1 ] dp[1] dp[1]值。这时,考虑枚举选哪个儿子的 d p [ 1 ] dp[1] dp[1]值。选完 d p [ 1 ] dp[1] dp[1]后,从剩下的儿子中,选前 k − 2 k-2 k−2个最大 d p [ 0 ] dp[0] dp[0]值就可以了。
(3)如果有子树的根节点,不与父节点相连,这时只需要多选一个儿子就可以了,方法同上,然后把这个结果并入到最终答案里面(取最大值)。
杭电6
1005 (hdu 6831)
1.分解前缀
在前缀任意位置插入加号,乘号和括号,相当于插入加号和乘号,优先级任意。因为加号和乘号是二元运算符,而且前缀运算结果是由子区间得到的,所以考虑合并区间的dp解决。
2.区间dp
首先知道前缀最长长度肯定是不超过
n
n
n的。
设
d
p
[
l
]
[
r
]
[
v
a
l
]
dp[l][r][val]
dp[l][r][val]为区间
[
l
,
r
]
[l, r]
[l,r]是否可以构成值
v
a
l
val
val,则:
d
p
[
l
]
[
m
i
d
]
[
v
a
l
1
]
dp[l][mid][val_1]
dp[l][mid][val1]和
d
p
[
m
i
d
+
1
]
[
r
]
[
v
a
l
2
]
dp[mid+1][r][val_2]
dp[mid+1][r][val2]可以转移到
d
p
[
l
]
[
r
]
[
v
a
l
1
+
v
a
l
2
]
dp[l][r][val_1+val_2]
dp[l][r][val1+val2]和
d
p
[
l
]
[
r
]
[
v
a
l
1
∗
v
a
l
2
]
dp[l][r][val_1*val_2]
dp[l][r][val1∗val2](注意还有区间
[
l
,
r
]
[l, r]
[l,r]不插入运算符的情况),这时,只需要枚举区间长度
l
e
n
len
len,区间左端点
l
l
l,插入运算符的位置
m
i
d
mid
mid,
v
a
l
1
val_1
val1和
v
a
l
2
val_2
val2,就可以把这个算出来了,时间复杂度为
O
(
n
5
)
O(n^5)
O(n5)。
3.打表判断进行优化
虽然上述做法时间复杂度爆炸,但是通过打表一小段数据猜测(玄学猜测),最长长度应该不会太长。经过多次试验打表后(从小到大开始试),发现长度为
11
11
11时,
n
≤
5000
n \le 5000
n≤5000内,除了
3
3
3和
7
7
7之外(
3
3
3和
7
7
7无论长度多长,必定无解),全部都有解。这时,前缀最长长度就可以设置为
11
11
11,时间复杂度为
O
(
1
1
3
∗
n
2
)
O(11^3*n^2)
O(113∗n2)。因为题目有
30
30
30组样例,所以考虑预处理
n
≤
5000
n \le 5000
n≤5000的答案,最后优化一下常数应该就行了(实测可以优化很大的常数)。
杭电8
1004 (hdu 6858)
1.题意
给出
m
m
m条边,有
q
q
q个询问,每次询问
[
l
,
r
]
(
1
≤
l
≤
r
≤
m
)
[l,r] \ (1 \le l \le r\le m)
[l,r] (1≤l≤r≤m)的边构成的图,是否有环。
2.尺取
设
R
i
R_i
Ri为:以第
i
i
i边为开始,能构成环的最小右端点。只要求出
R
i
R_i
Ri,每次询问时,只需要判断
r
r
r是否大于等于
R
i
R_i
Ri,就知道是否有环了。而且还有一个性质:
R
i
R_i
Ri是非递减的,所以可以考虑用尺取法。
3.性质证明
假设
R
i
>
R
i
+
1
R_i > R_{i+1}
Ri>Ri+1,那么区间
[
i
+
1
,
R
i
+
1
]
[i+1, R_{i+1}]
[i+1,Ri+1]的边构成的图肯定有环,即区间
[
i
,
R
i
+
1
]
[i, R_{i+1}]
[i,Ri+1]的边构成的图也是有环的(增加一条边还是有环)。那么,在计算
R
i
R_i
Ri的时候,因为是从左往右扫描的,所以肯定会先检测到
R
i
+
1
R_{i+1}
Ri+1,即
R
i
=
R
i
+
1
R_i=R_{i+1}
Ri=Ri+1。所以:
R
i
≤
R
i
+
1
R_i \le R_{i+1}
Ri≤Ri+1。
4.动态树
在尺取的过程中,要判定有环,只需要判定
R
i
R_i
Ri边的两个端点,是否连通,就可以了。要在添加边/删除边的同时,判定两个点是否连通,可以直接用动态树的模板解决。