前言
一个长链剖分的小trick
问题
如题,数据范围大概 1 0 5 10^5 105
思路
我们知道重链剖分是什么,即选择自己儿子中子树节点树最大的作为重儿子,其它儿子为轻儿子
而长链剖分则是选择儿子中子树深度最大的儿子为长儿子,其它为短儿子
这样进行树剖后就有一些很奇妙的性质,即:一个节点的kth祖先所在的链的链长大于k
我们考虑:对于每一条长度为
x
x
x的链,我们记录链头的每个kth祖先(
k
∈
[
1
,
x
]
k\in[1,x]
k∈[1,x]),以及链中的一个元素
我们发现,这个的记录时间复杂度和空间复杂度都是
O
(
n
)
\mathcal O(n)
O(n)的
那么,现在对于一个询问,求点
u
u
u的
k
k
kth祖先,如果我们能够
O
(
1
)
\mathcal O(1)
O(1)的找到其随便的一个
k
′
k'
k′th祖先(
k
′
>
⌊
k
2
⌋
k'>\left\lfloor\frac k2\right\rfloor
k′>⌊2k⌋),那我们就可以
O
(
1
)
\mathcal O(1)
O(1)的询问
k
k
kth祖先了(实际操作只需要在
k
′
k'
k′祖先的数组里进行访问即可)
考虑倍增,我们可以
O
(
n
l
o
g
n
)
\mathcal O(nlogn)
O(nlogn)预处理,这样我们就可以直接跳
2
i
2^i
2ith祖先了,在本题中,只要让
i
i
i满足
2
i
≤
k
,
2
i
+
1
>
k
2^i\le k,2^{i+1}>k
2i≤k,2i+1>k即可,这个东西我们也可以开个数组
O
(
n
)
\mathcal O(n)
O(n)预处理
综上,这个做法就能做到 O ( n l o g n ) \mathcal O(nlogn) O(nlogn)预处理, O ( 1 ) \mathcal O(1) O(1)询问了
代码
咕咕咕咕咕
好像没有模板题,所以就没了
update by 2019/8/4
碰到了一道需要用这来优化成
O
(
n
n
)
\mathcal O(n\sqrt n)
O(nn)的
luoguP3591 [POI2015]ODW
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<vector>
namespace fast_IO
{
const int IN_LEN=1000000,OUT_LEN=1000000;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
//#include<ctime>
#define rg register
typedef long long ll;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void Swap(T&a,T&b){T c=a;a=b;b=c;}
//template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
const int maxn=50001,maxm=100001;
std::vector<int>kth[maxn];
int head[maxn],nxt[maxm],tow[maxm],tmp;
inline void addb(const int u,const int v)
{
tmp++;
nxt[tmp]=head[u];
head[u]=tmp;
tow[tmp]=v;
}
int n,part,a[maxn],b[maxn],c[maxn],d[maxn];
int f[maxn][19],dep[maxn],son[maxn],nothead[maxn],F[maxn],top[maxn],las[maxn];
void dfs(const int u,const int fa)
{
dep[u]=1;
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa)continue;
f[v][0]=F[v]=u;
d[v]=d[u]+1,dfs(v,u);
if(dep[v]+1>dep[u])dep[u]=dep[v]+1,son[u]=v;
}
}
void dfs2(const int u,const int fa)
{
las[top[u]]=u;
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa)continue;
if(v==son[u])top[v]=top[u],dfs2(v,u);
else top[v]=v,dfs2(v,u);
}
}
int LCA(int u,int v)
{
if(d[u]<d[v])Swap(u,v);
int D=d[u]-d[v];
for(rg int i=0;i<=16;i++)if(D&(1<<i))u=f[u][i];
if(u==v)return u;
for(rg int i=16;i>=0;i--)if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
return F[u];
}
int qz[maxn][231],id[maxn];
void DFS(const int u,const int fa)
{
for(rg int j=1,v=F[u];j<=part;j++)qz[u][j]=qz[v][j]+a[u],v=F[v];
for(rg int i=head[u];i;i=nxt[i])
{
const int v=tow[i];
if(v==fa)continue;
DFS(v,u);
}
}
int ks[65537];
int fe(int u,int dis)
{
if(dis==0)return u;
u=f[u][ks[dis]];
dis-=1<<ks[dis];
if(u==0)return 0;
return kth[top[u]][id[u]+dis];
}
int main()
{
d[1]=1;
for(rg int i=1;i<=16;i++)
for(rg int j=1<<(i-1);j<(1<<i);j++)
ks[j]=i-1;
read(n),part=20;
for(rg int i=1;i<=n;i++)read(a[i]);
for(rg int i=1;i<n;i++)
{
int u,v;read(u),read(v);
addb(u,v),addb(v,u);
}
for(rg int i=1;i<=n;i++)read(b[i]);
for(rg int i=1;i<n;i++)read(c[i]);
dfs(1,0);
top[1]=1,dfs2(1,0);
for(rg int i=1;i<=16;i++)
for(rg int j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1];
for(rg int i=1;i<=n;i++)nothead[son[i]]=1;
for(rg int i=1;i<=n;i++)
if(!nothead[i])
{
std::vector<int>&t=kth[i];
const int S=dep[i]<<1;
t.resize(S+1);
for(rg int j=1,u=las[i];u&&j<=S;j++)
{
if(j<=dep[i])id[u]=j;
t[j]=u,u=F[u];
}
}
DFS(1,0);
for(rg int i=1;i<n;i++)
{
int u=b[i],v=b[i+1],step=c[i],lca=LCA(u,v);
if(step<=part)
{
int du=d[u]-d[lca],dv=d[v]-d[lca];
int ans=0;
int extra=du%step;du=du-extra+step;
ans+=qz[u][step]-qz[fe(u,du)][step];
dv+=extra;
if(dv==0)print(ans);
else
{
ans+=a[v];
extra=(dv-1)%step+1;
ans+=qz[fe(v,extra)][step]-qz[fe(v,dv)][step];
print(ans);
}
}
else
{
int ans=0;
while(d[u]>=d[lca])
{
ans+=a[u];
u=fe(u,step);
}
while(d[v]>d[lca])
{
ans+=a[v];
v=fe(v,step);
}
print(ans);
}
putchar('\n');
}
return flush(),0;
}
总结
挺巧妙的一个思路,在求kth祖先的次数比较多的时候可以使用