啊~~好难啊,听得有点懵逼,
不过,比之前好多了/开心^ _ ^
目录
- 动态规划例题选讲
- Gcd Counting(Codeforces 990G)
- You are given a tree(Codeforces 1039D)
- Vladislav and a Great Legend(Codeforces 1097G)
- Uniformly Branched Trees
- Multiplicity(Codeforces 1061C)
- Maximum Element(Codeforces 889C)
- Easy Problem(Codeforces 1096D)
- Kuro and Topological Parity(Codeforces 979E)
- Hero Meet Devil(HDOJ 4899)
- XHXJ's LIS(HDOJ 4352)
- Make It One(Codeforces 1043F)
动态规划例题选讲
Gcd Counting(Codeforces 990G)
【题目描述】
给出一颗
n
n
n 个节点的树,每个节点上有点权
a
i
a_i
ai 。
现在要你求出满足以下条件的最长的树上路径:
路径上经过节点 (包括两个端点) 点权的
G
c
d
Gcd
Gcd 和不等于
1
1
1 。
【数据范围】
n
⩽
2
×
1
0
5
n\leqslant 2\times 10^5
n⩽2×105 。
1
⩽
a
i
⩽
2
×
1
0
5
1\leqslant a_i\leqslant 2\times 10^5
1⩽ai⩽2×105 。
【解析】
只要不互质就行了,因此我们不用具体考虑
G
c
d
Gcd
Gcd 的值,还不用考虑重复计算。
对于每个节点
u
u
u ,维护
D
p
[
u
]
[
v
]
Dp[u][v]
Dp[u][v] 表示
u
u
u 往下挂出的点权都能被
v
v
v 整除的最长链。
而
v
v
v 只用取整除
a
i
a_i
ai 的质数,个数很少,很容易转移,然后边转移边更新全局答案。
【核心代码】(盗个图):
You are given a tree(Codeforces 1039D)
【题目描述】
给出一颗
n
n
n 个节点的树,对于
1
∼
n
1\sim n
1∼n 间的每一个数
k
k
k ,你需要求出:
最多能选出多少条互不相交的路径,每条路径的长度都为
k
k
k 。
【数据范围】
n
⩽
1
0
5
n\leqslant 10^5
n⩽105 。
【解析】
首先,我们先说一下贪心:在一个子树中,尽可能最大化完整的路径条数;其次最大化未完成的链的长度。
在进行合并时,先尝试儿子的
D
p
Dp
Dp 值中第二维最大值与次大值能否拼成一条完整的链(长度和
⩾
k
\geqslant k
⩾k),若不行再选取一条最长的链向上延伸。时间复杂度:
Θ
(
n
)
\Theta(n)
Θ(n) 。
令
f
[
k
]
f[k]
f[k] 表示单次对于给定的
k
k
k 的答案,那么显然有:
f
[
k
]
⩽
n
k
f[k]\leqslant\frac n k
f[k]⩽kn 。
T
i
p
s
:
f
\mathscr{Tips:f}
Tips:f中不同的取值个数是
Θ
(
n
)
\Theta(\sqrt n)
Θ(n) 级别的。
而
f
f
f 显然是单调的,所以我们只要在每个边界上二分即可。
总时间复杂度:
Θ
(
n
n
log
n
)
\Theta(n\sqrt n\log n)
Θ(nnlogn) 。
Vladislav and a Great Legend(Codeforces 1097G)
【题目描述】
给出一颗
n
n
n 个节点的树
T
T
T 。
对于其中任意一个非空节点集合
X
X
X ,定义
f
(
X
)
f(X)
f(X) 为包含这些点的最小连通子树的边数。
给出一个正整数
k
k
k ,求:
∑
x
⊆
{
1
,
2
,
…
,
n
}
,
X
!
=
∅
(
f
(
X
)
)
k
\sum_{x\subseteq\{1,2,…,n\},X!=\empty}(f(X))^k
x⊆{1,2,…,n},X!=∅∑(f(X))k答案对
1
0
9
+
7
10^9+7
109+7 取模。
【数据范围】
2
⩽
n
⩽
1
0
5
2\leqslant n\leqslant 10^5
2⩽n⩽105 。
1
⩽
k
⩽
200
1\leqslant k\leqslant 200
1⩽k⩽200 。
【解析】
(
f
(
X
)
)
k
(f(X))^k
(f(X))k 很难直接算,于是我们把它拆开来:
x
k
=
∑
i
=
0
k
(
x
i
)
×
i
!
×
S
(
k
,
i
)
x^k=\sum_{i=0}^k\binom x i\times i!\times S(k,i)
xk=i=0∑k(ix)×i!×S(k,i)其中
S
(
k
,
i
)
S(k,i)
S(k,i) 是第二类斯特林数,表示将
k
k
k 个元素拆分成
i
i
i 个集合的方案数。
【第二类斯特林数算法核心代码】
Stirling[0][0]=1;
for(int i=1;i<=k;++i)
{
for(int j=1;j<=i;++j)
{
Stirling[i][j]=(Stirling[i-1][j-1]+(ll)Stiring[i-1][j]*j%mod)%mod;
}
}
接下来我们把上式带回到原来题目里面的式子里去:
A
n
s
=
∑
X
∑
i
=
0
k
(
f
(
X
)
i
)
×
i
!
×
S
(
k
,
i
)
=
∑
i
=
0
k
i
!
×
S
(
k
,
i
)
⋅
∑
X
(
f
(
X
)
i
)
\begin{aligned} Ans&=\sum_X\sum_{i=0}^k\binom{f(X)}i\times i!\times S(k,i)\\ &=\sum_{i=0}^ki!\times S(k,i)\cdot\sum_X\binom{f(X)}i \end{aligned}
Ans=X∑i=0∑k(if(X))×i!×S(k,i)=i=0∑ki!×S(k,i)⋅X∑(if(X))我们在外面枚举了
i
i
i 之后,要求的东西就是:
所
有
非
空
点
集
对
应
的
生
成
树
标
记
了
i
条
边
的
方
案
数
之
和
所有非空点集对应的生成树标记了i条边的方案数之和
所有非空点集对应的生成树标记了i条边的方案数之和是不是看上去特别像树上背包,事实上树上背包也就够了。
令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示以
i
i
i 为根的所有生成树当中标记了
j
j
j 条边的方案数,由于点集大小之类的不影响答案,所以不同点集对应的生成树可以一并考虑,得到:
d
p
[
x
]
[
i
]
+
=
d
p
[
x
]
[
j
]
∗
d
p
[
v
]
[
i
−
j
]
dp[x][i]+=dp[x][j]*dp[v][i-j]
dp[x][i]+=dp[x][j]∗dp[v][i−j]【代码展示】
#include<bits/stdc++.h>
#define ll long long
#define ud using namespace std
ud;
const int maxn=1e5+100;
const int mod=1e9+7;
int n,k;
int top=0,first[maxn];
int mul[maxn],Size[maxn]={};
ll dp[maxn][210],g[210],s[210][210],f[maxn];
struct Tree
{
int y;
int next;
}e[maxn<<1];
void add(int x,int y)
{
e[++top].y=y;
e[top].next=first[x];
first[x]=top;
}
ll jia(ll x,ll y)
{
x+=y;
if(x>=mod)
x-=mod;
return x;
}
inline long long read()
{
long long sum=0,flag=1;
char c;
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=-1;
for(;c>='0'&&c<='9';c=getchar())sum=(sum<<1)+(sum<<3)+c-'0';
return sum*flag;
}
void dfs(int x,int fa)
{
Size[x]=1;
dp[x][0]=2;
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)
continue;
dfs(y,x);
memset(f,0,sizeof(f));
for(int p=0;p<=min(Size[x],k);++p)
{
for(int q=0;q<=min(k-p,Size[y]);++q)
{
f[p+q]=jia(f[p+q],dp[x][p]*dp[y][q]%mod);
}
}
for(int j=0;j<=k;++j)
dp[x][j]=f[j];
for(int j=0;j<=k;++j)
g[j]=jia(g[j],mod-dp[y][j]);
Size[x]+=Size[y];
}
for(int i=0;i<=k;++i)
g[i]=jia(g[i],dp[x][i]);
for(int i=k;i>=1;--i)
dp[x][i]=jia(dp[x][i],dp[x][i-1]);
dp[x][1]=jia(dp[x][1],mod-1);
}
int main()
{
n=read();
k=read();
mul[0]=1;
for(int i=1;i<=k;++i)
{
mul[i]=(ll)mul[i-1]*i%mod;
}
s[0][0]=1;
for(int i=1;i<=k;++i)
{
for(int j=1;j<=i;++j)
{
s[i][j]=jia(s[i-1][j-1],s[i-1][j]*j%mod);
}
}
for(int i=1;i<n;++i)
{
int xx,yy;
xx=read();
yy=read();
add(xx,yy);
add(yy,xx);
}
dfs(1,0);
int ans=0;
for(int i=0;i<=k;++i)
ans=jia(ans,(ll)mul[i]*s[k][i]%mod*g[i]%mod);
printf("%d\n",ans);
}
至于时间复杂度,看似是
Θ
(
n
k
2
)
\Theta(nk^2)
Θ(nk2) ,实际是
Θ
(
n
k
)
\Theta(nk)
Θ(nk) 。
我们分三种情况考虑:
∙
s
i
z
e
[
u
]
⩾
k
&
s
i
z
e
[
v
]
⩾
k
,
单
次
合
并
复
杂
度
Θ
(
k
2
)
\bullet size[u]\geqslant k\And size[v]\geqslant k,单次合并复杂度\Theta(k^2)
∙size[u]⩾k&size[v]⩾k,单次合并复杂度Θ(k2)
此时每次相当于合并了两堆大小至少为
k
k
k 的石头,至多有
n
k
\frac n k
kn 堆。
因此,合并次数为
Θ
(
n
k
)
\Theta(\frac n k)
Θ(kn) ,总复杂度
Θ
(
n
k
)
\Theta(nk)
Θ(nk) 。
∙
s
i
z
e
[
u
]
⩾
k
&
s
i
z
e
[
v
]
<
k
,
单
次
合
并
复
杂
度
Θ
(
k
×
s
i
z
e
[
v
]
)
\bullet size[u]\geqslant k\And size[v]<k,单次合并复杂度\Theta(k\times size[v])
∙size[u]⩾k&size[v]<k,单次合并复杂度Θ(k×size[v])
这种情况下
s
i
z
e
[
v
]
size[v]
size[v] 之和不超过
n
n
n ,因此总复杂度
Θ
(
n
k
)
\Theta(nk)
Θ(nk) 。
u
u
u 和
v
v
v 交换的情况也是如此。
∙
s
i
z
e
[
u
]
<
k
&
s
i
z
e
[
v
]
<
k
\bullet size[u]<k\And size[v]<k
∙size[u]<k&size[v]<k
这相当于是正常的树上背包,树的大小为
Θ
(
k
)
\Theta(k)
Θ(k) ,众所周知它的复杂度是
Θ
(
k
2
)
\Theta(k^2)
Θ(k2)
我们考虑每两个点之间的贡献只会产生在它们的
l
c
a
lca
lca 处最多有
n
k
\frac n k
kn 棵这样的树,因此总复杂度
Θ
(
n
k
)
\Theta(nk)
Θ(nk) 。
综上可知,时间复杂度为 Θ ( n k ) \Theta(nk) Θ(nk) 。
Uniformly Branched Trees
【题目描述】
如果两棵树可以通过重标号后变为完全相同,那么它们就是 同构 的。
将中间节点定义为度数大于
1
1
1 的节点。
计算有
n
n
n 个节点,其中所有的中间节点度数都为
d
d
d 的互不同构的树的数量。
答案对大质数取模。
【数据范围】
1
⩽
n
⩽
1000
1\leqslant n\leqslant 1000
1⩽n⩽1000 。
2
⩽
d
⩽
10
2\leqslant d\leqslant 10
2⩽d⩽10 。
1
0
8
⩽
m
o
d
⩽
1
0
9
10^8\leqslant mod\leqslant 10^9
108⩽mod⩽109 。
【解析】
Multiplicity(Codeforces 1061C)
【题目描述】
有个长度为
n
n
n 的序列
a
a
a ,你需要统计
a
a
a 中有多少个棒棒的子序列。
一个序列
b
b
b 被定义为棒棒的,当且仅当:
对
于
序
列
中
每
一
个
位
置
i
,
b
i
都
能
被
i
整
除
对于序列中每一个位置i,b_i都能被i整除
对于序列中每一个位置i,bi都能被i整除答案对
1
0
9
+
7
10^9+7
109+7 取模。
【数据范围】
n
⩽
1
0
5
n\leqslant 10^5
n⩽105 。
1
⩽
a
i
⩽
1
0
6
1\leqslant a_i\leqslant 10^6
1⩽ai⩽106 。
【解析】
Maximum Element(Codeforces 889C)
【题目描述】
有个神仙写了个序列求
m
a
x
max
max ,它长下面这样:
现在他比较关心的是出错的情况,请你算出有多少个
1
∼
n
1\sim n
1∼n 的排列在这个函数的计算下答案不为
n
n
n 。
答案对
1
0
9
+
7
10^9+7
109+7 取模。
【数据范围】
n
,
k
⩽
1
0
6
n,k\leqslant 10^6
n,k⩽106 。
【解析】
Easy Problem(Codeforces 1096D)
【题目描述】
给出一个长度为
n
n
n 的字符串,如果这个字符串中含有子序列“
h
a
r
d
hard
hard ”,那么这个字符串就很
h
a
r
d
hard
hard 。你不希望这个字符串很
h
a
r
d
hard
hard ,所以想要从中删掉几个字符使它不
h
a
r
d
hard
hard 。
例:
h
a
r
d
,
h
z
a
z
r
z
d
,
h
a
a
a
a
a
r
d
hard,hzazrzd,haaaaard
hard,hzazrzd,haaaaard 都很
h
a
r
d
hard
hard ,而
h
a
r
,
h
a
r
t
,
d
r
a
h
har,hart,drah
har,hart,drah 不
h
a
r
d
hard
hard 。
删掉在原字符串中的第
i
i
i 个字符需要花费的代价为
a
i
a_i
ai ,请求出最小代价。
【数据范围】
1
⩽
n
⩽
1
0
5
1\leqslant n\leqslant 10^5
1⩽n⩽105 。
1
⩽
a
i
⩽
998244353
1\leqslant a_i\leqslant 998244353
1⩽ai⩽998244353 。
【解析】
Kuro and Topological Parity(Codeforces 979E)
【题目描述】
有一个
n
n
n 个点的图,有一些点的颜色给定,另一些可以随意确定。另外,还可以在图上连一些从编号较小的节点向编号较大的节点的边。
对于一个确定的图,统计图中节点颜色黑白交错的路径条数,如果奇偶性
=
p
=p
=p ,那么该图就被定义为好图。
请你求出好图的个数。
答案对
1
0
9
+
7
10^9+7
109+7 取模。
【数据范围】
1
⩽
n
⩽
50
1\leqslant n\leqslant 50
1⩽n⩽50 。
p
∈
{
0
,
1
}
p\in\{0,1\}
p∈{0,1} 。
【解析】
Hero Meet Devil(HDOJ 4899)
【题目描述】
给出一个字符串
S
S
S ,这个字符串只由
A
,
C
,
G
,
T
A,C,G,T
A,C,G,T 四个字母组成。
对于每个
1
∼
∣
S
∣
1\sim|S|
1∼∣S∣ 中的每一个
i
i
i ,求出满足以下条件的字符串
T
T
T 的个数:
1、长度为
m
m
m 。
2、只由
A
,
C
,
G
,
T
A,C,G,T
A,C,G,T 四个字母组成。
3、
L
C
S
(
S
,
T
)
=
i
LCS(S,T)=i
LCS(S,T)=i .
答案对
1
0
9
+
7
10^9+7
109+7 取模。
【数据范围】
∣
S
∣
⩽
15
,
m
⩽
1000
|S|\leqslant 15,m\leqslant 1000
∣S∣⩽15,m⩽1000 。
【解析】
XHXJ’s LIS(HDOJ 4352)
【题目描述】
求
[
L
,
R
]
[L,R]
[L,R] 中,各位数字组成的序列的
L
I
S
LIS
LIS 恰好为
k
k
k 的数字个数。
【数据范围】
T
⩽
1
0
4
,
0
<
L
⩽
R
<
2
63
−
1
T\leqslant 10^4,0<L\leqslant R<2^{63}-1
T⩽104,0<L⩽R<263−1 。
1
⩽
k
⩽
10
1\leqslant k\leqslant 10
1⩽k⩽10 。
【解析】
Make It One(Codeforces 1043F)
【题目描述】
有一个长度为
n
n
n 的序列
a
a
a ,一个
a
a
a 中的子集被定义为好的当且仅当它其中元素的
G
c
d
Gcd
Gcd 为
1
1
1 。
求最小的好的子集的大小。
或判断不存在这样的子集。
【数据范围】
1
⩽
n
,
a
i
⩽
3
×
1
0
5
1\leqslant n,a_i\leqslant3\times10^5
1⩽n,ai⩽3×105 。
【解析】