[JZOJ5336] 提米树
来源:互联网 发布:网络小精灵国语版58网 编辑:程序博客网 时间:2024/06/07 00:38
题目大意
给一颗n个点的以1为根树,点权i值为a[i]。
定义相邻的叶子(x,y):x,y都是叶子节点(无儿子),而且在dfs序中,x,y之间没有其他叶子节点。
定义一棵树的贡献:所有叶子节点的权值减去所有相邻叶子的特殊贡献。
(x,y)的特殊贡献定义为,x到y路径上,除了x,y之外所有点的权值的最大值。
定义剪枝:如果一个点z的儿子都是叶子,剪枝可以把z的所有儿子去掉。
不限制剪枝次数,求树的最大贡献。
分析
题意十分长,原题面更是掺杂了其他东西,审题要小心。
每次剪枝相当于把原来的叶子浓缩成他们的父亲,那么考虑到剪枝的特点,可以发现,对于原来的每个叶子x,1到x的路径必须要有一个点要被保留为叶子。
我们考虑使用DP,当然你也可以先尝试树形DP然后发现他的可行性。
设f[i]表示考虑了dfn为1~i的节点,此时这颗树(有可能未完成)的贡献。考虑转移,我们顺着DFS序,考虑从一个叶子到另外一个叶子的路径上所有点的转移/被转移。
(盗图,懒得做了)
可以看出是lca为分割点的两部分,左边的部分是被转移的点,右边是要转移到的点,lca及以上是不涉及转移的。我们暴力枚举两边的点进行转移,f[new]=max{f[old]-cost}+a[new],cost即为路径上权值最大值。这样就是
考虑优化,我们设h[old]表示从father[old],即old的父亲,到lca的路径的最大权值,H[new]表示从father[new]到lca路径的最大权值,那么原式变成f[new]=max{f[old]-max(h[old],H[new])}。注意到h和H的单调性,对于一个点new,把所有old点分为h[old]<=H[new]和h[old]>H[new]两部分,那么两部分的点都是连续的,就可以分开维护来更新f[new]了。
代码
#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#include<set> using namespace std;typedef long long ll;typedef double db;#define fo(i,j,k) for(i=j;i<=k;i++)#define fd(i,j,k) for(i=j;i>=k;i--)const int N=2e5+5,mo=1e9+7,mx=1e9;int dis[N],fa[N],b[N],nxt[N],fst[N],tt,a[N],d[N],e[N],f[N],g[N],h[N],w[N],mxf,H,r,i,j,x,y,n,ans;void cr(int x,int y){ tt++; b[tt]=y; nxt[tt]=fst[x]; fst[x]=tt;}void dfs(int x){ //dfn[x]=++td; dis[x]=dis[fa[x]]+1; if (!fst[x]) d[++d[0]]=x; for(int p=fst[x];p;p=nxt[p]) dfs(b[p]);}int main(){ freopen("t3.in","r",stdin);// freopen("t3.out","w",stdout); scanf("%d\n",&n); fo(i,1,n) { scanf("%d %d",a+i,&x); fo(j,1,x) { scanf("%d",&y); cr(i,y); fa[y]=i; } } dfs(1); x=d[1]; while (x) f[x]=a[x],x=fa[x]; fo(i,1,d[0]-1) { w[0]=e[0]=0; x=d[i];y=d[i+1]; while (dis[x]>dis[y]) e[++e[0]]=x,x=fa[x]; while (dis[x]<dis[y]) w[++w[0]]=y,y=fa[y]; while (x!=y) { e[++e[0]]=x,x=fa[x]; w[++w[0]]=y,y=fa[y]; } fo(j,1,w[0]/2) swap(w[j],w[w[0]-j+1]); fo(j,1,e[0]+1) h[j]=0; fd(j,e[0],1) h[j]=max(h[j+1],a[fa[e[j]]]); H=mxf=g[0]=-mx; fo(j,1,e[0]) g[j]=max(g[j-1],f[e[j]]-h[j]); r=e[0]; fo(j,1,w[0]) { H=max(H,a[fa[w[j]]]); while (r&&H>h[r]) { mxf=max(mxf,f[e[r]]); r--; } f[w[j]]=max(mxf-H,g[r])+a[w[j]]; } } x=d[d[0]]; while(x) ans=max(f[x],ans),x=fa[x]; printf("%d\n",ans);}
- [JZOJ5336] 提米树
- 【JZOJ5336】【NOIP2017提高模拟】提米树(DP、前缀和)
- 【JZOJ5336】【NOIP2017提高A组模拟8.24】提米树
- jzoj5336 【NOIP2017提高A组模拟8.24】提米树 (dfs序dp,奇异姿势dp)
- 【NOIP2017提高A组模拟8.24】提米树
- dsu on tree
- activiti表结构讲解
- C++ set
- Flash builder 编译编译提示java heap space解决方法
- 小波变换
- [JZOJ5336] 提米树
- JavaSE 网络编程
- SpringMVC拦截器实现登录验证的例子
- 解决Ubuntu 切换root失败问题
- odl-boron-sr3之hello rpc
- 找到B字符串在A字符串中出现的所有位置的集合
- hdu 6161
- 自己搭建自动化巡检系统(一) 环境准备系统连接gns3
- numpy中函数nditer的作用