bzoj1791,P4381-[IOI2008]Island【基环树,树形dp,单调队列dp,树的直径】
生活随笔
收集整理的這篇文章主要介紹了
bzoj1791,P4381-[IOI2008]Island【基环树,树形dp,单调队列dp,树的直径】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
評測記錄:https://www.luogu.org/recordnew/lists?uid=52918&pid=P4381
題目大意
有n個島,n條無向邊(保證每個島都有邊連到)。走過的路和島不可以重走,可以坐船。
坐船要求之前沒有任何使用過的船加上道路可以到達那個點才可以坐船。
求最長可以走多遠。
解題思路
首先這是一棵基環樹森林,根據乘船的規定其實就是每棵基環樹只可以走一次。這時候我們就可以發現答案就是每棵基環樹的直徑之和。然后我們考慮如何計算直徑。
將環看作根。我們發現答案只有兩種可能
我們考慮不經過根的,計算出fxf_xfx?(fxf_xfx?表示x子樹中離x最遠的點的距離),然后用求樹的直徑的方法求出根以下每棵子樹的直徑并記錄。然后我們計算經過根的。
假設環的節點為sss集合,那么答案就是max(fsi+fsj+disi~j)max(f_{s_i}+f_{s_j}+dis_{i\sim j})max(fsi??+fsj??+disi~j?)
dis表示環上i到j的最遠距離。
我們可以通過單調隊列dp計算出答案。
code
#include<cstdio> #include<algorithm> #include<queue> #include<iostream> #define N 1000010 #define lls long long using namespace std; struct node{int to,next,w; }a[2*N]; int n,x,y,tot,t; lls ls[N],in[N],cr[2*N],v[N],k[N],f[N],d[N],dis[2*N],ans,q[2*N]; void addl(int x,int y,int w)//加邊 {a[++tot].to=y;a[tot].w=w;a[tot].next=ls[x];ls[x]=tot;in[y]++; } void dfs(int now,int k){v[now]=k;for (int i=ls[now];i;i=a[i].next){int y=a[i].to;if(!v[y]) dfs(y,k);} }//標記聯通塊 void topsort(){int l=0,r=0;for (int i=1;i<=n;i++) if(in[i]==1) q[++r]=i;while(l<r) {int now=q[++l];for (int i=ls[now];i;i=a[i].next){int y=a[i].to;if(in[y]>1){in[y]--;d[v[now]]=max(d[v[now]],f[now]+f[y]+a[i].w);f[y]=max(f[y],f[now]+a[i].w);if(in[y]==1) q[++r]=y;}}} }//拓撲排序求環 void dp(int t,int x){int m=0,y=x,i;do{cr[++m]=f[y];in[y]=1;for(i=ls[y];i;i=a[i].next){if(in[a[i].to]>1){dis[m+1]=dis[m]+a[i].w;y=a[i].to;break;}}}while(i);//預處理環的數據if(m==2){int l=0;for (int i=ls[y];i;i=a[i].next) if(a[i].to==x) l=max(l,a[i].w);d[t]=max(d[t],f[x]+f[y]+l);return;}//特批for(int i=ls[y];i;i=a[i].next){if(a[i].to==x) {dis[m+1]=dis[m]+a[i].w;break;}}//連接首尾for (int i=1;i<=m;i++){cr[i+m]=cr[i];dis[m+i]=dis[m+1]+dis[i];}//復制一份放在后面int l=1,r=0;q[++r]=1;for (int i=2;i<2*m;i++){while(l<=r&&i-q[l]>=m)l++;d[t]=max(dis[i]-dis[q[l]]+cr[i]+cr[q[l]],d[t]);while(l<=r&&cr[q[r]]+dis[i]-dis[q[r]]<=cr[i])r--;q[++r]=i;}//單調隊列dp } int main(){scanf("%d",&n);for (int i=1;i<=n;i++){scanf("%d%d",&x,&y);addl(i,x,y);addl(x,i,y);}for (int i=1;i<=n;i++) if (!v[i]) dfs(i,++t);topsort();for (int i=1;i<=n;i++){if(in[i]>1&&!k[v[i]]) {k[v[i]]=1;dp(v[i],i);ans+=d[v[i]];}}printf("%lld",ans); }總結
以上是生活随笔為你收集整理的bzoj1791,P4381-[IOI2008]Island【基环树,树形dp,单调队列dp,树的直径】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国卫星传回太阳表面多种爆发景象,计划开
- 下一篇: P5022-旅行【基环树,dfs】