简述
用于优化于深度状态有关的 dp
转移,时空复杂度均为 O(n)
。其中每个节点的状态是用指针分配内存。
CF1009F Dominant Indices
题意:设 d(u,x)
为 u
子树中到 u
距离为 x
的节点数。对于每个点,求一个最小的 k
,使得 d(u,k)
最大。
#include<bits/stdc++.h>
using namespace std;
const int mx=1e6+5;
vector<int> g[mx];
//内存池
int buf[mx];
//指针动态分配空间
int *f[mx],*now=buf,dep[mx],son[mx],ans[mx];
int n;
void dfs1(int x,int fa) {
for(auto y:g[x]) {
if(y==fa) continue;
dfs1(y,x);
if(dep[y]>dep[son[x]]) son[x]=y;
}
dep[x]=dep[son[x]]+1;
}
void dfs2(int x,int fa) {
f[x][0]=1;
if(son[x]) {
f[son[x]]=f[x]+1;
dfs2(son[x],x);
ans[x]=ans[son[x]]+1;
}
for(auto y:g[x]) {
if(y==fa||y==son[x]) continue;
f[y]=now; now+=dep[y];
dfs2(y,x);
for(int j=1;j<=dep[y];j++) {
f[x][j]+=f[y][j-1];
if(f[x][j]>f[x][ans[x]]||f[x][j]==f[x][ans[x]]&&j<ans[x]) ans[x]=j;
}
}
//细节问题
if(f[x][ans[x]]==1) ans[x]=0;
}
signed main() {
scanf("%d",&n);
for(int i=1;i<n;i++) {
int x,y; scanf("%d%d",&x,&y);
g[x].push_back(y),g[y].push_back(x);
}
dfs1(1,0);
f[1]=now; now+=dep[1];
dfs2(1,0);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
[POI2014]HOT-Hotels
好难啊。。。
定义 f[u][j]
为以 u
为根的子树,距离为 j
的节点个数。g[u][j]
定义为下图 (x,y)
点对个数:
枚举子节点 v
转移:
g[u][j]*f[v][j-1]->ans
g[v][j]*f[u][j+1]->ans
f[u][j]->f[v][j+1]
比较棘手的是 g[u][j]
的转移,分类讨论:
(x,y)
在同一子树内,g[v][j]->g[u][j-1]
(x,y)
不在同一子树内,用f[]
转移:f[v][j-1]*f[u][j]->g[u][j]
注意g[]
比f[]
先转移。朴素转移是O(n)
,时空复杂度O(n^2)
。
可以用长链剖分优化。具体地,先继承重儿子的状态,考虑 ans+=g[u][0]
。再暴力转移轻儿子。注意 g
指针要前移。
时空复杂度均为 O(n)
。
总结:本题第一眼是换根 dp
,但是无法优化。本题巧妙利用状态设置+长链剖分,将距离作为状态,成功优化到线性。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mx=1e6+5;
vector<int> G[mx];
int buf[mx<<2];
int *f[mx],*g[mx],*now=buf,dep[mx],son[mx];
int n; int res;
//长链剖分优化 dp
//uodate : 2021-07-23
void dfs1(int u,int fa) {
for(auto v:G[u]) {
if(v==fa) continue;
dfs1(v,u);
if(dep[v]>dep[son[u]]) son[u]=v;
}
dep[u]=dep[son[u]]+1;
}
void dfs2(int u,int fa) {
f[u][0]=1;
if(son[u]) {
f[son[u]]=f[u]+1;
g[son[u]]=g[u]-1; //g[v][j]->g[u][j-1]
dfs2(son[u],u);
}
res+=g[u][0];
for(auto v:G[u]) {
if(v==fa||v==son[u]) continue;
f[v]=now; now+=dep[v]<<1;
g[v]=now; now+=dep[v]<<1;
dfs2(v,u);
for(int j=1;j<=dep[v];j++) {
res+=g[u][j]*f[v][j-1]+g[v][j]*f[u][j-1];
}
for(int j=0;j<dep[v];j++) {
g[u][j]+=g[v][j+1];
}
for(int j=1;j<=dep[v];j++) {
g[u][j]+=f[v][j-1]*f[u][j];
}
for(int j=1;j<=dep[v];j++) {
f[u][j]+=f[v][j-1];
}
}
}
signed main() {
scanf("%lld",&n);
for(int i=1;i<n;i++) {
int x,y; scanf("%lld%lld",&x,&y);
G[x].push_back(y),G[y].push_back(x);
}
dfs1(1,0);
f[1]=now; now+=dep[1]<<1;
g[1]=now; now+=dep[1]<<1;
dfs2(1,0);
printf("%lld",res);
}