[WC2010]重建計劃(分數規劃+點分治+單調佇列)
阿新 • • 發佈:2018-12-22
題目大意:給定一棵樹,求一條長度在L到R的一條路徑,使得邊權的平均值最大。
題解
樹上路徑最優化問題,不難想到點分治。
如果沒有長度限制,我們可以套上01分數規劃的模型,讓所有邊權減去mid,求一條路徑長度非負。
現在考慮有L和R的限制,就是我們在拼接兩條路徑的時候,每條路徑能夠匹配的是按深度排序後一段連續區間,我們只需要維護區間最大值。
然後隨著深度的單調變化,這個區間在滑動,這就變成了滑動視窗問題。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #defineN 100002 #define inf 2e9 #define Re register using namespace std; typedef long long ll; const double eps=1e-4; double mid,ans,ma,deep[N],man[N]; int tot,head[N],dp[N],q[N],minl,maxl,size[N],maxdeep,root,sum,n,dep[N],que[N],L,R; bool vis[N],visit[N]; inline ll rd(){ ll x=0;char c=getchar();bool f=0;while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } struct edge{int n,to,l;}e[N<<1]; inline void add(int u,int v,int l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;} void getsize(int u,int fa){ size[u]=1; for(Re int i=head[u];i;i=e[i].n)if(e[i].to!=fa&&!vis[e[i].to]){ int v=e[i].to; getsize(v,u);size[u]+=size[v]; } } inline int mx(int a,int b){return a>b?a:b;} inline double maxx(double a,double b){return a>b?a:b;} void getroot(int u,int fa){ dp[u]=0;size[u]=1; for(Re int i=head[u];i;i=e[i].n)if(!vis[e[i].to]&&e[i].to!=fa){ int v=e[i].to; getroot(v,u);size[u]+=size[v]; dp[u]=mx(dp[u],size[v]); } dp[u]=mx(dp[u],sum-size[u]); if(dp[u]<dp[root])root=u; } void getdeep(int u,int fa){ maxdeep=mx(maxdeep,dep[u]); for(Re int i=head[u];i;i=e[i].n)if(!vis[e[i].to]&&e[i].to!=fa){ int v=e[i].to;deep[v]=deep[u]+e[i].l-mid;dep[v]=dep[u]+1; getdeep(v,u); } } void getcalc(int u,int fa){ man[dep[u]]=maxx(man[dep[u]],deep[u]); for(Re int i=head[u];i;i=e[i].n)if(!vis[e[i].to]&&e[i].to!=fa){ int v=e[i].to;getcalc(v,u); } } inline bool getcheck(int u){ maxdeep=0;bool tag=0; for(Re int i=head[u];i;i=e[i].n)if(!vis[e[i].to]){ int v=e[i].to;deep[v]=e[i].l-mid;dep[v]=1; getdeep(v,u); int h=1,t=1;que[h]=v;visit[v]=1; while(h<=t){ int x=que[h++]; for(int j=head[x];j;j=e[j].n){ int v=e[j].to; if(!vis[v]&&!visit[v]&&v!=u)que[++t]=v,visit[v]=1; } } int p=0;L=1;R=0;q[++R]=0; for(Re int i=t;i>=1;--i){ int x=que[i];visit[x]=0; while(p+dep[x]<maxl&&p<maxdeep){ int x=++p; while(L<=R&&man[x]>=man[q[R]])R--; q[++R]=x; } while(L<=R&&q[L]+dep[x]<minl)L++; if(L<=R&&deep[x]+man[q[L]]>=0)tag=1; } getcalc(v,u); } for(Re int i=1;i<=maxdeep;++i)man[i]=-inf; return tag; } inline void getans(int u){ double l=ans,r=ma; while(r-l>eps){ mid=(l+r)/2.0; if(getcheck(u)){ans=mid;l=mid;}else r=mid; } } void solve(int u){ getans(u);vis[u]=1; for(Re int i=head[u];i;i=e[i].n)if(!vis[e[i].to]){ int v=e[i].to; root=n+1;sum=size[v]; getroot(v,u);//getsize(root,0); solve(root); } } int main(){ n=rd();minl=rd();maxl=rd();int u,v,w; for(int i=1;i<=n;++i)man[i]=-inf; ma=-1e9;ans=1e9; for(Re int i=1;i<n;++i){ u=rd();v=rd();w=rd();ma=maxx(ma,(double)w);ans=min(ans,(double)w); add(u,v,w);add(v,u,w); } dp[root=n+1]=n;sum=n; getroot(1,0);//getsize(root,0); solve(root); printf("%.3lf",ans); return 0; }