前言

一个长链剖分的小trick

问题

如题,数据范围大概10510^5105

思路

我们知道重链剖分是什么,即选择自己儿子中子树节点树最大的作为重儿子,其它儿子为轻儿子
而长链剖分则是选择儿子中子树深度最大的儿子为长儿子,其它为短儿子

这样进行树剖后就有一些很奇妙的性质,即:一个节点的kth祖先所在的链的链长大于k
我们考虑:对于每一条长度为xxx的链,我们记录链头的每个kth祖先(k∈[1,x]k\in[1,x]k∈[1,x]),以及链中的一个元素
我们发现,这个的记录时间复杂度和空间复杂度都是O(n)\mathcal O(n)O(n)的

那么,现在对于一个询问,求点uuu的kkkth祖先,如果我们能够O(1)\mathcal O(1)O(1)的找到其随便的一个k′k'k′th祖先(k′>⌊k2⌋k'>\left\lfloor\frac k2\right\rfloork′>⌊2k​⌋),那我们就可以O(1)\mathcal O(1)O(1)的询问kkkth祖先了(实际操作只需要在k′k'k′祖先的数组里进行访问即可)
考虑倍增,我们可以O(nlogn)\mathcal O(nlogn)O(nlogn)预处理,这样我们就可以直接跳2i2^i2ith祖先了,在本题中,只要让iii满足2i≤k,2i+1>k2^i\le k,2^{i+1}>k2i≤k,2i+1>k即可,这个东西我们也可以开个数组O(n)\mathcal O(n)O(n)预处理

综上,这个做法就能做到O(nlogn)\mathcal O(nlogn)O(nlogn)预处理,O(1)\mathcal O(1)O(1)询问了

代码

咕咕咕咕咕
好像没有模板题,所以就没了
update by 2019/8/4
碰到了一道需要用这来优化成O(nn)\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祖先的次数比较多的时候可以使用

长链剖分:O(nlogn)预处理O(1)求kth祖先相关推荐

  1. [COGS2652]秘术「天文密葬法」-长链剖分-01分数规划

    秘术「天文密葬法」 题目说明: 路径的长度是点数 所有整数都是正整数 已添加一句话题意 [题目描述] 永琳需要协助紫解决异变! 在某个满月的夜晚,幻想乡的结界出现了异常,虽然目前还没有找到原因,不过有 ...

  2. 树链剖分之长链剖分 详解 题目整理

    树链剖分 题目中出现的树链剖分一般分为两种,重链剖分和长链剖分 重链剖分:选择子树最大的儿子, 将其归入当前点所在 的同一条重链 长链剖分:选择向下能达到的深 度最深的儿子,将其归 入当前点所在的同一 ...

  3. 【luogu P5903】【模板】树上 k 级祖先(长链剖分)

    [模板]树上 k 级祖先 题目链接:luogu P5903 题目大意 给你一棵树,要你在线 O(1) 求一个点的 k 级祖先. 思路 这个我们可以用长链剖分来做,从而可以做到预处理 O ( n log ...

  4. BZOJ4381[POI2015]Odwiedziny——分块+长链剖分

    题目描述 给定一棵n个点的树,树上每条边的长度都为1,第i个点的权值为a[i]. Byteasar想要走遍这整棵树,他会按照某个1到n的全排列b走n-1次,第i次他会从b[i]点走到b[i+1]点,并 ...

  5. UOJ#284-快乐游戏鸡【长链剖分,线段树】

    正题 题目链接:https://uoj.ac/problem/284 题目大意 nnn个点的一棵树,每个点有一个wiw_iwi​表示至少死亡wiw_iwi​次才能通过这个点,否则就会死亡.只能往子节点 ...

  6. CF570D-Tree Requests【长链剖分】

    正题 题目链接:https://www.luogu.com.cn/problem/CF570D 题目大意 nnn个点的一棵树,每个节点有字母,每次询问一个节点xxx的子树中深度为kkk的所有点的字母能 ...

  7. 【BZOJ4543】【POI2014】Hotel加强版(长链剖分)

    传送门 题意:求树上满足三点之间距离两两相等的三元组个数 n≤1e5n\le 1e5n≤1e5 原题数据是n≤5000n\le5000n≤5000 考虑怎么做 f[u][i]f[u][i]f[u][i ...

  8. 长链剖分 总结 【知识点】

    附上自学博客链接 https://www.cnblogs.com/Khada-Jhin/p/9576403.html 概念: 长链剖分类似于重链剖分,只是选择链的标准: 不是 子树节点最多的儿子,而是 ...

  9. 长链剖分(知识点整理+板子总结)

    思路来源 https://blog.nowcoder.net/n/5eaebd22f5f846838c637bc337cc1ee9 https://blog.csdn.net/litble/artic ...

最新文章

  1. 如何读取多个文件,文件后缀名不一致,不过类似source.1 source.2 source.3等
  2. 深度学习必备---用Keras和直方图均衡化---数据增强
  3. TypeError: Caught TypeError in DataLoader worker process 0.
  4. 万字长文解读运营商搏击5G:一场比拼财力的三国杀
  5. (接口)让僵冷的翅膀飞起来---从实例谈OOP、工厂模式和重构
  6. Android中图片的目录
  7. java多线程构造函数_Java多线程编程经验谈
  8. java高级应用:线程池全面解析
  9. 笔记本电脑cpu排行_2020年笔记本电脑推荐总篇(详细参数amp;选购推荐)
  10. 采购订单单价金额屏蔽
  11. java 数组数据类型_java基本数据类型和数组
  12. 1. SVN (1)
  13. MapReduce学习要点
  14. Visio_Premium_2010_VOL 和Project_Pro_2010。
  15. 软考程序员Java答题速成_软考程序员考试下午题解答方法与技巧
  16. Windows设置自动开关机
  17. PHP调用系统声卡,什么是声卡
  18. 微信向移动开放平台又迈进了一大步:微信开放平台更新
  19. supervisor安装使用
  20. Firewall 防火墙常用命令

热门文章

  1. 缓存redis的使用方案
  2. 记一次CentOS Install Docker 报错
  3. apollo分布式配置中心原理
  4. hystrix-dashboard
  5. FastDFS简介和安装
  6. c语言怎么减去一个16进制数,一个简单的有关问题:像这样的16进制数怎么转换得到...
  7. IDEA使用技巧整理
  8. 使用pip install出现超时警告的解决方法
  9. python 输出一个 5*5的 三角形_GitHub标星3W+,80个Python案例,带你轻松玩转Python学习!...
  10. Linux watch 监控系统状态