AT_ddcc2020_final_d Pars/ey

news/发布时间2024/5/19 0:53:44

AT_ddcc2020_final_d Pars/ey

重工业题。

找环然后树形 DP 是显然的,先考虑断开环上的边怎么做。

把环复制一遍放在结尾,记 \(sum_i\) 为环长的前缀和,\(f_i\) 为该子树内的最长根链的长度,问题变为每次给定一个区间,要求找到 \(i,j(i>j)\) 使得 \(sum_i-sum_j+f_i+f_j\) 最大,可以使用线段树实现。注意要与同一棵树里的直径取 max(大概有 \(\mathcal O(n)\) 做法吧)

然后考虑树边如何处理。先求出不删边时基环树的直径,如果是树内的直径那么删其他边不会对答案造成影响,所以只需计算该子树。直径经过了环可以对两个端点所在的树计算两次。

断了树边后基环树的直径有 2 种情况:直径至少有一端在这棵树里和不在这棵树里。两端都在树外面是好处理的,对于第一种情况可以类似换根的方法处理。

image.png

1.直径为蓝色或红色的路径。

蓝色的路径是好处理的,对于每棵子树维护子树内直径即可。

对于红色,因为换根到儿子时红色路径的长度可以继承父亲的,所以只需要一个变量记录当前红色路径的长度。

设当前最长的红色路径长度为 \(dead\),设 \(in_{k,0}\) 表示以 \(k\) 为根的子树内不经过该点的最大直径, \(in_{k,1}\) 表示和 \(in_{k,0}\) 不在同一棵子树里的最大直径,这样向下搜索时如果 \(to\)\(in_{k,0}\) 所在的子树,用 \(in_{k,1}\) 更新 \(dead\),否则用 \(in_{k,0}\) 更新。

另一种情况,如下图:这条红色路径的可以在 \(5\) 号点是更新,具体的,类似换根 DP 的思路,我们记 \(maxn\) 表示当前点向父亲走能走到的最远的距离(树内),用 \(maxn+f_k\) 更新红色路径的长度。但是更新时会出现问题:\(f_5\) 本身由 \(f_6\) 转移而来,所以我们不仅要记录最长根链的长度,还要记录和最长根链不在同一子树内的次长根链的长度,如果 \(f_{to,0}+v_i\)\(f_{k,0}\) 相等,用 \(f_{k,1}+maxn\) 更新红色路径,否则用 \(f_{to,0}\) 更新。

image.png

另另一种情况,如下图:断掉了 \((1,5)\)\(dead\) 还可以由 \(1\) 子树内不经过 \(5\) 的两条链拼接而成,所以我们不仅要记录次大根链,还要记录次次大根链,如果断的边是 \(f_{k,0}\) 的链,用 \(f_{k,1}+f_{k,2}\) 更新 \(dead\),其他情况同理。

image.png

2.直径另一端不在该树内。

这种情况是 trival 的,记录 \(up\) 表示该点到根的距离,每次向下走维护到根的距离的最大值和当前到根的距离,每次还是判断最大次大值与子树的值的关系即可。

细节很多,非常难写。

int n,cnt=1,dead,tot,max2,all,now,now2,maxu,maxi,maxj,ans[800001],pos[800001],id[800001],ide[800001],in[800001][2],f[800001][3],ex[800001],vis[800001],loop[800001],head[800001],to[800001],nex[800001],v[800001],sum[800001],val[800001];
inline void add(int x,int y,int z){to[++cnt]=y,v[cnt]=z,nex[cnt]=head[x],head[x]=cnt;}
stack<int> st;
namespace Segment
{struct Node{int maxn,max1,max2,l,l1,r,r2;};struct{int l,r;Node nd;}t[3400001];Node operator +(const Node nd1,const Node nd2){Node nd;nd.l=nd1.l1,nd.r=nd2.r2,nd.maxn=nd1.max1+nd2.max2; if(nd1.maxn>nd.maxn)nd.maxn=nd1.maxn,nd.l=nd1.l,nd.r=nd1.r;if(nd2.maxn>nd.maxn)nd.maxn=nd2.maxn,nd.l=nd2.l,nd.r=nd2.r;if(nd1.max1>nd2.max1)nd.l1=nd1.l1;else nd.l1=nd2.l1;nd.max1=max(nd1.max1,nd2.max1);if(nd1.max2>nd2.max2)nd.r2=nd1.r2;else nd.r2=nd2.r2;nd.max2=max(nd1.max2,nd2.max2);return nd;}void build(int p,int l,int r){t[p].l=l,t[p].r=r;if(l==r)return t[p].nd.l=t[p].nd.l1=t[p].nd.r=t[p].nd.r2=l,val[l]+=val[l-1],t[p].nd.max2=f[loop[l]][0]+val[l-1],t[p].nd.max1=f[loop[l]][0]-val[l-1],void();int mid=l+((r-l)>>1);build(p*2,l,mid),build(p*2+1,mid+1,r),t[p].nd=t[p*2].nd+t[p*2+1].nd;}void change(int p,int x,int y){if(t[p].l==t[p].r)return t[p].nd.max2=y+val[x],t[p].nd.max1=y-val[x],void();if(x<=t[p*2].r)change(p*2,x,y);else change(p*2+1,x,y);t[p].nd=t[p*2].nd+t[p*2+1].nd;}Node ask(int p,int l,int r){if(l<=t[p].l&&r>=t[p].r)return t[p].nd;if(l>t[p*2].r)return ask(p*2+1,l,r);if(r<=t[p*2].r)return ask(p*2,l,r);return ask(p*2,l,r)+ask(p*2+1,l,r);}void print(int p){if(!t[p].l)return;write(t[p].l),write(t[p].r),write(t[p].nd.max1),write(t[p].nd.max2),write(t[p].nd.maxn,'\n');print(p*2),print(p*2+1);}
}
using namespace Segment;
void findloop(int k,int from)
{st.e(k);for(int i=head[k];i;i=nex[i]){if(i==(from^1))continue;if(sum[to[i]]){int y;sum[k]=v[i],ide[k]=i;do vis[loop[++tot]=y=st.top()]=1,id[tot]=ide[y],val[tot]=sum[y],st.pop();while(y!=to[i]);}else sum[k]=v[i],ide[k]=i,findloop(to[i],i);if(tot)return;}st.pop();
}
void dfs(int k,int from)
{for(int i=head[k];i;i=nex[i]){if(i==(from^1)||vis[to[i]])continue;dfs(to[i],i);int tmp=max(in[to[i]][0],f[to[i]][0]+f[to[i]][1]);if(tmp>=in[k][0])in[k][1]=in[k][0],in[k][0]=tmp;else if(tmp>=in[k][1])in[k][1]=tmp;if(f[to[i]][0]+v[i]>=f[k][0])f[k][2]=f[k][1],f[k][1]=f[k][0],f[k][0]=f[to[i]][0]+v[i];else if(f[to[i]][0]+v[i]>=f[k][1])f[k][2]=f[k][1],f[k][1]=f[to[i]][0]+v[i];else if(f[to[i]][0]+v[i]>=f[k][2])f[k][2]=f[to[i]][0]+v[i];}
}
inline void dfs2(int k,int maxn,int from,int up)
{int pre=maxu,ppr=dead;
//		write(k),write(dead,'\n');for(int i=head[k],upon;i;i=nex[i]){if(vis[to[i]]||i==(from^1))continue;if(in[to[i]][0]==in[k][0]||f[to[i]][0]+f[to[i]][1]==in[k][0])dead=max(dead,in[k][1]);else dead=max(dead,in[k][0]);
//			write(to[i]),write(in[k][0]),write(in[k][1]),write(dead,'\n');if(f[to[i]][0]+v[i]==f[k][0])dead=max({dead,f[k][1]+maxn,f[k][1]+f[k][2]});else if(f[to[i]][0]+v[i]==f[k][1])dead=max({dead,f[k][0]+maxn,f[k][0]+f[k][2]});else dead=max({dead,f[k][0]+f[k][1],maxn+f[k][0]});
//			write(to[i]),write(dead,'\n');if(f[to[i]][0]+v[i]==f[k][0])upon=f[k][1],maxu=max(maxu,up+f[k][1]),dfs2(to[i],max(maxn,f[k][1])+v[i],i,up+v[i]);else upon=f[k][0],maxu=max(maxu,up+f[k][0]),dfs2(to[i],max(maxn,f[k][0])+v[i],i,up+v[i]);ans[pos[i]]=max({dead,in[to[i]][0],f[to[i]][0]+f[to[i]][1],now+max(maxu,upon+up),now2});if(f[to[i]][0]+v[i]==f[k][0])ans[pos[i]]=max({ans[pos[i]],f[k][1]+f[k][2],f[k][1]+maxn});else if(f[to[i]][0]+v[i]==f[k][1])ans[pos[i]]=max({ans[pos[i]],f[k][0]+f[k][2],f[k][0]+maxn});else ans[pos[i]]=max({ans[pos[i]],f[k][0]+f[k][1],f[k][0]+maxn});maxu=pre,dead=ppr;}
//		write(k),write(dead,'\n');
}
inline Node calc2()
{Node nd1,nd2;nd1.maxn=0;for(int i=2,j=1;i<=tot*2;++i){while((val[i]-val[j])*2>all)++j;nd2=ask(1,j,i);if(nd2.maxn>nd1.maxn)nd1=nd2;}return nd1;
}
inline void calc(int ex)
{if(ex>tot)ex-=tot;change(1,ex,0),change(1,ex+tot,0),dead=0;now=-INF,now2=calc2().maxn;for(int i=1;i<=tot;++i)if(i!=ex)now2=max({now2,in[loop[i]][0],f[loop[i]][0]+f[loop[i]][1]});for(int i=ex+tot-1;i>ex;--i)if((val[i]-val[ex])*2<=val[tot+1])now=max(now,val[i]+f[loop[i]][0]-val[ex]);for(int i=ex+1;i<ex+tot;++i)if((val[ex+tot]-val[i])*2<=val[tot+1])now=max(now,f[loop[i]][0]-val[i]+val[ex+tot]);dfs2(loop[ex],0,0,0);change(1,ex,f[loop[ex]][0]),change(1,ex+tot,f[loop[ex]][0]);
}
inline void mian()
{read(n);int x,y,z,pos2=0;Node nd;for(int i=1;i<=n;++i)read(x,y,z),add(x,y,z),add(y,x,z),pos[cnt]=pos[cnt-1]=i;findloop(1,0),reverse(loop+1,loop+tot+1),reverse(val+1,val+tot+1),reverse(id+1,id+1+tot),id[0]=id[tot];for(int i=1;i<=tot;++i)loop[i+tot]=loop[i],val[i+tot]=val[i],now=i,dfs(loop[i],0),max2=max({max2,in[loop[i]][0],f[loop[i]][0]+f[loop[i]][1]}),max(in[loop[i]][0],f[loop[i]][0]+f[loop[i]][1])==max2?pos2=i:0;build(1,1,tot*2),all=val[tot];for(int i=tot*2;i>=1;--i)val[i]=val[i-1];nd=calc2();for(int i=1;i<=tot;++i)ans[pos[id[i-1]]]=max(max2,ask(1,i,i+tot-1).maxn);if(nd.maxn<=max2)calc(pos2);else calc(nd.l),calc(nd.r);for(int i=1;i<=n;++i)if(!ans[i])write(max(nd.maxn,max2),'\n');else write(ans[i],'\n');
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ulsteruni.cn/article/23767311.html

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

数据采集与融合技术实践作业一

作业一: 要求:用requests和BeautifulSoup库方法定向爬取给定网址(http://www.shanghairanking.cn/rankings/bcur/2020 )的数据,屏幕打印爬取的大学排名信息。 输出信息:排名 学校名称 省市 学校类型 总分1 清华大学 北京 852.5 综合2......实验: import requests # 方式…

! [rejected] master - master (fetch first)

! [rejected] master -> master (fetch first)原因 Git仓库中已经有一部分代码,所以它不允许你直接把你的代码覆盖上去。 远程仓库和本地仓库存在差异。 一般都是因为你在码云创建的仓库有ReadMe文件,而本地没有,造成本地和远程的不同步, 解决方法: 方法一、同步…

【算法】算法性能分析

1 时间复杂度 1.1 知识点 时间复杂度是一个函数,它定性描述该算法的运行时间。 通常会估算算法的操作单元数量来代表程序消耗的时间。假设算法的问题规模为n,那么操作单元数量便用函数f(n)来表示,随着数据规模n的增大,算法执行时间的增长率和f(n)的增长率相同,这称作为算法…

读高性能MySQL(第4版)笔记14_备份与恢复(中)

备份与恢复1. 在线备份 2. 离线备份 2.1. 关闭MySQL做备份是最简单、最安全的 2.2. 所有获取一致性副本的方法中最好的 2.3. 损坏或不一致的风险最小 2.4. 根本不用关心InnoDB缓冲池中的脏页或其他缓存 2.5. 不需要担心数据在尝试备份的过程中被修改 2.5.1. 服务器不对应用提供…

#0 | 蜕

New Born.总会下意识躲闪 不能预知的未来 越是奋不顾身 越会被眼泪覆盖 渴望能变得从容 会让心有恃无恐 寻找一种状态 重新对明天期盼回忆在慢慢浮现 那个单纯的小孩 走在孤独边界 想象天空的蔚蓝 渴望能赢得信赖 填满心所有缺口 让失落的勇气 还能再次被点燃我在黑暗中游走 呼…