很久很久以前 e r k k i e r k k o erkkierkko erkkierkko给我们分享了一下这个东西,好像和 d s u o n t r e e dsu\ on\ tree dsu on tree的思想很像,都是长链直接转移,短链暴力算
其中有一个很有意思的东西就是指针动态分配数组空间,因为一个儿子的信息刚好是父亲往左或者往右移一步,比如说深度,如果一个 d p dp dp数组是以深度为下标的话,就可以让指针左移右移那样来分配空间,而短链长度加起来不会超过 n n n,所以说短链可以直接开新空间。
直接上例题吧
CF1009F Dominant Indices
记录
f
[
u
]
[
i
]
f[u][i]
f[u][i]表示
u
u
u为根的子树中到根距离为
i
i
i的点的个数
f
[
u
]
[
i
]
=
∑
v
f
[
v
]
[
i
−
1
]
f[u][i]=\sum_{v}f[v][i-1]
f[u][i]=∑vf[v][i−1],如此就可以用上述做法来解决空间问题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
int n,cnt,head[N],to[N<<1],nxt[N<<1];
int len[N],tmp[N],son[N],*f[N],*id=tmp,ans[N];
inline void add(int x,int y){
to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;
}
void dfs(int u,int fa){
for(int i=head[u];i;i=nxt[i])
if(to[i]!=fa){
dfs(to[i],u);
if(len[to[i]]>len[son[u]]) son[u]=to[i];
}
len[u]=len[son[u]]+1;
}
void DP(int u,int fa){
f[u][0]=1;
if(son[u]) f[son[u]]=f[u]+1,DP(son[u],u),ans[u]=ans[son[u]]+1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i]; if(v==fa||v==son[u])continue;
f[v]=id; id+=len[v]; DP(v,u);
for(int j=1;j<=len[v];j++){
f[u][j]+=f[v][j-1];
if(f[u][j]>f[u][ans[u]]) ans[u]=j;
else if(f[u][j]==f[u][ans[u]]) ans[u]=min(ans[u],j);
}
}
if(f[u][ans[u]]==1) ans[u]=0;
}
int main(){
n=rd();
for(int i=1;i<n;i++){
int x=rd(),y=rd();
add(x,y); add(y,x);
}
dfs(1,0); f[1]=id; id+=len[1];
DP(1,0);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
bzoj4543: [POI2014]Hotel加强版
同样的方法
设
f
[
u
]
[
i
]
f[u][i]
f[u][i]表示
u
u
u为根的子树,到
u
u
u距离为
i
i
i的点的个数,
g
[
u
]
[
i
]
g[u][i]
g[u][i]表示到中心的距离为
d
d
d且
u
u
u到中心距离为
d
−
i
d-i
d−i
f
[
u
]
[
0
]
=
1
f[u][0]=1
f[u][0]=1
a
n
s
+
=
g
[
u
]
[
0
]
ans+=g[u][0]
ans+=g[u][0]
枚举出边,一边枚举一边更新保证不重复计算,每次枚举出边的时候再枚举 i i i
f [ u ] [ i ] + = f [ v ] [ i − 1 ] f[u][i]+=f[v][i-1] f[u][i]+=f[v][i−1]
g [ u ] [ i − 1 ] + = g [ v ] [ i ] g[u][i-1]+=g[v][i] g[u][i−1]+=g[v][i]
g [ u ] [ i + 1 ] + = f [ u ] [ i + 1 ] × f [ v ] [ i ] g[u][i+1]+=f[u][i+1]\times f[v][i] g[u][i+1]+=f[u][i+1]×f[v][i]
a n s + = f [ u ] [ i − 1 ] × g [ v ] [ i ] + g [ u ] [ i + 1 ] × f [ v ] [ i ] ans+=f[u][i-1]\times g[v][i]+g[u][i+1]\times f[v][i] ans+=f[u][i−1]×g[v][i]+g[u][i+1]×f[v][i]
然后再用指针动态分配空间的方法就好了, f , g f,g f,g的转移刚好是反着的
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100005
#define LL long long
using namespace std;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int n,cnt,head[N],to[N<<1],nxt[N<<1];
int len[N],son[N];
LL ans,*f[N],*g[N],tmp[N<<2],*id=tmp;
inline void add(int x,int y){
to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;
}
void dfs(int u,int fa){
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v!=fa){
dfs(v,u);
if(len[v]>len[son[u]]) son[u]=v;
}
}
len[u]=len[son[u]]+1;
}
void DP(int u,int fa){
if(son[u]) f[son[u]]=f[u]+1,g[son[u]]=g[u]-1,DP(son[u],u);
f[u][0]=1; ans+=g[u][0];
for(int i=head[u];i;i=nxt[i]){
int v=to[i]; if(v==fa||v==son[u]) continue;
f[v]=id; id+=len[v]<<1; g[v]=id; id+=len[v]<<1; DP(v,u);
for(int j=0;j<len[v];j++){
if(j) ans+=f[u][j-1]*g[v][j];
ans+=g[u][j+1]*f[v][j];
}
for(int j=0;j<len[v];j++){
g[u][j+1]+=f[u][j+1]*f[v][j];
if(j) g[u][j-1]+=g[v][j];
f[u][j+1]+=f[v][j];
}
}
}
int main(){
n=rd();
for(int i=1;i<n;i++){
int x=rd(),y=rd();
add(x,y); add(y,x);
}
dfs(1,0);
f[1]=id; id+=len[1]<<1; g[1]=id; id+=len[1]<<1; DP(1,0);
printf("%lld\n",ans);
return 0;
}
另一个应用:求
k
k
k级祖先
首先
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)处理出倍增数组,然后进行长链剖分,记下链长和链顶,因为有性质
x
的
k
级
祖
先
y
所
在
的
链
的
长
度
一
定
>
k
x的k级祖先y所在的链的长度一定>k
x的k级祖先y所在的链的长度一定>k,所以就可以在每个链顶挂两个
v
e
c
t
o
r
vector
vector,一个是向上
l
e
n
len
len个点,一个是向下
l
e
n
len
len个点,查询的时候只要二进制拆分,分类讨论一下就好了,复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)预处理+
O
(
1
)
O(1)
O(1)查询
例题:lxhgww的奇思妙想
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 300005
#define K 20
using namespace std;
inline int rd(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int n,m,dep[N],len[N],p[N][K],son[N],mxd[N],top[N],hb[N],ans;
bool vis[N];
vector<int> e[N],up[N],dw[N];
inline void add(int x,int y){e[x].push_back(y);}
void dfs1(int u,int fa,int depth){
mxd[u]=dep[u]=depth;
p[u][0]=fa;
for(int j=1;j<K;j++)
if(p[u][j-1]) p[u][j]=p[p[u][j-1]][j-1];
else break;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa) continue;
dfs1(v,u,depth+1);
if(mxd[v]>mxd[son[u]])
son[u]=v,mxd[u]=mxd[v];
}
}
void dfs2(int u,int t,int ll){
top[u]=t; len[u]=ll;
if(son[u]){
dfs2(son[u],t,ll+1);
len[u]=len[son[u]];
}
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==p[u][0] || v==son[u]) continue;
dfs2(v,v,1);
}
}
inline int query(int u,int k){
if(k>dep[u]) return 0;
if(!k) return u;
u=p[u][hb[k]],k^=(1<<hb[k]);
if(!k) return u;
if(dep[u]-dep[top[u]]==k) return top[u];
if(dep[u]-dep[top[u]]>k) return dw[top[u]][dep[u]-dep[top[u]]-k-1];
return up[top[u]][k-(dep[u]-dep[top[u]])-1];
}
int main(){
n=rd();
for(int i=1;i<n;i++){
int x=rd(),y=rd();
add(x,y); add(y,x);
}
dfs1(1,0,1); dfs2(1,1,1);//预处理
for(int i=1;i<=n;i++){
int t=top[i];
if(!vis[t]){//每个链顶向上和向下
vis[t]=1;
int l=0,now=t;
while(l<len[t] && now){
now=p[now][0];
l++;
up[t].push_back(now);
}
l=0;now=t;
while(l<len[t]){
now=son[now];
l++;
dw[t].push_back(now);
}
}
}
int mx=1;
for(int i=1;i<=n;i++){
if(i>>mx&1) mx++;
hb[i]=mx-1;//hightbit
}
m=rd();
while(m--){
int x=rd()^ans,y=rd()^ans;
printf("%d\n",ans=query(x,y));
}
return 0;
}