一种利用重链剖分优化一类树形动态规划空间复杂度的方法
来源:互联网 发布:ubuntu rm删除多个文件 编辑:程序博客网 时间:2024/06/16 02:27
Origin
在某一场GDOI模拟赛上,一道好好的点分治题目,本蒟蒻强行大力优化暴力碾了过去。这题中我算法的瓶颈不在时间复杂度,而在于空间复杂度。为了解决这个问题,我想到了一种使用重链剖分来优化空间复杂度的方法。
Problem
首先我们要明确一下这种方法的适用范围:
一个点对该点父亲的贡献,可以直接利用该点已知的信息以及该点父亲已有的信息计算得,不需要依赖该点的兄弟或者其它点的信息。
比如我们现在要讨论的这一道题目:
给定一棵
这个显然如果我们处理出每个点为顶点然后颜色集合二进制状态为
怎么压空间呢?考虑这样一种算法:我们建一个内存池动态分配空间(每次会给一个节点分配大小为
这样的话,我的空间复杂度是多少呢?我们假定在DFS过程中,一个点第一个访问的儿子叫做偏爱儿子,那么可以发现,这个算法的空间复杂度是这个点到根的路径上,不是父亲的偏爱儿子的点的个数。也就是说,我DFS过程中对儿子的选择,决定了我空间复杂度的多少。
仔细看看这个“偏爱儿子”的定义,就可以发现,这个空间复杂度其实相当于我用某一种方式对这棵树进行树链剖分,然后一个点到根路径上轻边的条数。如果我使用的算法是重链剖分,那么轻边的条数就是
Implement
#include <iostream>#include <cstdio>#include <cctype>#include <queue>using namespace std;typedef long long LL;int read(){ int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar(); while (isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}const int N=50050;const int V=17;const int K=10;const int S=1<<K;const int E=N<<1;int fid[N],fa[N],size[N],last[N],col[N],lgc[N];int nxt[E],tov[E];queue<int> avail;int f[V][S];int g[S];int n,k,s,tot;LL ans;void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}void dfs(int x){ size[x]=1; for (int i=last[x],y;i;i=nxt[i]) if ((y=tov[i])!=fa[x]) { fa[y]=x,dfs(y),size[x]+=size[y]; if (!lgc[x]||size[lgc[x]]<size[y]) lgc[x]=y; }}void pre(){for (int i=0;i<V;++i) avail.push(i);}void makeroom(int x){fid[x]=avail.front(),avail.pop();}void clear(int x){ int id=fid[x]; for (int s_=0;s_<s;++s_) f[id][s_]=0; fid[x]=-1,avail.push(id);}void process(int id){ for (int s_=0;s_<s;++s_) g[s_]=f[id][s_]; for (int i=0,l,len;i<k;++i) { len=1<<i+1,l=len>>1; for (int j=0;j<s;j+=len) for (int x=j;x<j+l;++x) g[x]+=g[x+l]; }}void calc(int x){ int id; fid[x]=-1; if (!lgc[x]) makeroom(x); else { calc(lgc[x]); for (int i=last[x],y;i;i=nxt[i]) if ((y=tov[i])!=fa[x]&&y!=lgc[x]) calc(y); } ++f[id=fid[x]][1<<col[x]],process(id); for (int s_=0;s_<s;++s_) ans+=1ll*f[id][s_]*g[(s-1)^s_]; if (fa[x]) { int y=fa[x]; if (fid[y]==-1) makeroom(y); int id_=fid[y],c=1<<col[y]; for (int s_=0;s_<s;++s_) f[id_][s_|c]+=f[id][s_],ans-=1ll*f[id][s_]*g[(s-1)^(s_|c)]; } clear(x);}int main(){ freopen("colortree.in","r",stdin),freopen("colortree.out","w",stdout); n=read(),k=read(),s=1<<k; for (int i=1;i<=n;++i) col[i]=read()-1; for (int i=1,x,y;i<n;++i) x=read(),y=read(),insert(x,y),insert(y,x); fa[1]=0,dfs(1),pre(),calc(1); printf("%lld\n",ans); fclose(stdin),fclose(stdout); return 0;}
Postscript
这个算法是我在比赛中的脑洞成果,其实我个人认为其应用范围恐怕很狭窄,毕竟对于这一类题目,如果我树形dp的空间都会被卡的话,这个时间复杂度估计也特别大了。一般情况下是不会用到的。不过作为一个有趣的发现,就在这里记录一下吧。
如果各位dalao对这个算法有什么更深入的想法,欢迎留言交流。
Update
发现了一道使用这个思路的题目:[HDU5511]Minimum Cut-Cut
- 一种利用重链剖分优化一类树形动态规划空间复杂度的方法
- 动态规划_矩阵连乘的空间复杂度优化
- 一类动态规划的四边形优化
- 找零钱问题中动态优化的空间复杂度优化
- 关于一类动态规划题的总结
- 针对矩阵的一类动态规划处理
- 动态规划解最长回文子序列并优化空间复杂度
- 动态规划解最长回文子序列(非最长回文字串)并优化空间复杂度
- 动态规划初步-01背包问题&&一维数组(空间复杂度优化)
- POJ1141 动态规划,空间优化
- 【Leetcod 动态规划】 子数组最大和一类的问题
- 关于数塔一类问题的动态规划问题
- 动态规划——triangle空间复杂度O(n)
- 动态规划的优化
- 动态规划的优化
- Palindrome 动态规划算法 +空间优化
- 类斐波那契问题中动态规划的时间复杂度优化
- 利用servlet规范,一种实现动态路径的方法
- 小文件合并存储问题
- IP协议的相关技术
- Roman to Integer
- [C++基础]指针函数与函数指针<详细讲解>
- 笔试面试算法经典--判断二叉树是否是平衡二叉树(Java)
- 一种利用重链剖分优化一类树形动态规划空间复杂度的方法
- Jack-server出错:out of memory error,try increasing heap size
- Linux 下使用awk处理数据并写入数据库
- 排序算法之插入排序
- 不可重复读和幻读的一些区别
- 数组之sort Array中,找出给定数字出现的次数
- 初识.net界面程序(4)——数组排序和计算练习
- HDU 2546 饭卡
- 初识进程