Problem
Solution
二分答案,把路徑權值都減去mid,那麼就變成了能不能選出一條長度權值大於等於0的路徑。
考慮點分治,那麼我們就只需要考慮拼接兩棵子樹。這是一個有長度限制以深度爲下標的dp。這裏用單調隊列優化。記錄 表示之前遍歷過的子樹中深度爲 到根的最大權值, 爲當前的,單調隊列優化拼接即可。
但是仔細想想,如果 如果很長, 卻很短,如此構造可以使點分治退化成。爲了複雜度正確,必須使的長度與當前處理的子樹大小相關,那麼我們按子樹最深的深度從小到大處理。這樣的複雜度就不會退化。
時間複雜度
然後卡常纔是這道題的噁心之處。筆者的代碼用了這麼些卡常的方法:
- 預處理出點分樹,並把子樹先排序好,用vector存起來
- 減少調用dfs
- 當前樹的大小小於等於 時直接退出
- 減少實數運算
由於預處理了點分樹,所以你還可以通過逆序枚舉分治重心來應對毒瘤數據。
Code
#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>
#define mk(x,y) make_pair(x,y)
#define fr first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=100010;
const double eps=1e-7,INF=1e9;
template <typename Tp> int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> void read(Tp &x)
{
x=0;char ch=getchar();int f=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') ch=getchar(),f=1;
while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();
if(f) x=-x;
}
struct data{
int v,nxt;double w;
data(const int _v=0,const double _w=0.0,const int _nxt=0){v=_v;w=_w;nxt=_nxt;}
}edge[maxn<<1];
int n,L,R,p,lim,now,head[maxn],dep[maxn],dsz[maxn],tp[maxn];
double ans,f[maxn],g[maxn],w[maxn];
deque<int> q;
vector<pii> son[maxn];
vector<pii>::iterator itr;
namespace Build{
const static int INF=0x3f3f3f3f;
int p,sn,rt,top,sz[maxn],mx[maxn],dis[maxn],mark[maxn],stk[maxn];
int cmp(const int &a,const int &b){return dis[a]<dis[b];}
void getrt(int x,int pre)
{
sz[x]=1;mx[x]=dis[x]=0;
for(int i=head[x];i;i=edge[i].nxt)
if(!mark[edge[i].v]&&edge[i].v^pre)
{
getrt(edge[i].v,x);
sz[x]+=sz[edge[i].v];
getmax(mx[x],sz[edge[i].v]);
getmax(dis[x],dis[edge[i].v]);
}
getmax(mx[x],sn-sz[x]);
if(mx[x]<mx[rt]) rt=x;
++dis[x];
}
void dfs(int x,int now)
{
mark[x]=1;getrt(x,x);dep[x]=now;dsz[x]=sz[x];top=0;
for(int i=head[x];i;i=edge[i].nxt)
if(!mark[edge[i].v])
stk[++top]=edge[i].v;
sort(stk+1,stk+top+1,cmp);
for(int i=1;i<=top;i++) son[x].push_back(mk(stk[i],dis[stk[i]]));
for(int i=head[x];i;i=edge[i].nxt)
if(!mark[edge[i].v])
{
sn=sz[edge[i].v];rt=0;
getrt(edge[i].v,edge[i].v);
dfs(rt,now+1);
}
}
}
int cmp(const int &a,const int &b){return dep[a]<dep[b];}
void insert(int u,int v,double w)
{
edge[++p]=data(v,w,head[u]);head[u]=p;
edge[++p]=data(u,w,head[v]);head[v]=p;
}
void input()
{
int u,v,w;
read(n);read(L);read(R);
for(int i=1;i<n;i++)
{
read(u);read(v);read(w);
insert(u,v,(double)w);
}
Build::mx[0]=Build::INF;Build::sn=n;
Build::getrt(1,1);Build::dfs(Build::rt,1);
for(int i=1;i<=n;i++) tp[i]=i;
sort(tp+1,tp+n+1,cmp);
}
void dfs2(int x,int pre,int deep,double sum)
{
getmax(g[deep],sum);
for(int i=head[x];i;i=edge[i].nxt)
if(dep[edge[i].v]>now&&edge[i].v^pre)
dfs2(edge[i].v,x,deep+1,sum+edge[i].w);
}
int work(int x)
{
if(dsz[x]<=L) return 0;
int lf=0,lg,ptr,tot=(int)son[x].size();
now=dep[x];
for(int i=head[x];i;i=edge[i].nxt)
if(dep[edge[i].v]>dep[x])
w[edge[i].v]=edge[i].w;
itr=son[x].end()-1;
for(int i=itr->se;i;i--) f[i]=g[i]=-INF;
for(itr=son[x].begin();itr!=son[x].end();++itr)
{
lg=itr->se;dfs2(itr->fr,x,1,w[itr->fr]);ptr=min(lf,R-1);
if(lf+lg>=L)
{
while(!q.empty()) q.pop_front();
for(int i=1;i<=lg;i++)
{
while(ptr&&i+ptr>=L)
{
while(!q.empty()&&f[q.back()]<f[ptr]) q.pop_back();
q.push_back(ptr);--ptr;
}
while(!q.empty()&&i+q.front()>R) q.pop_front();
if(!q.empty()&&g[i]+f[q.front()]>=0) return 1;
}
}
lf=lg;
for(int j=1;j<=lf;j++) getmax(f[j],g[j]),g[j]=-INF;
}
for(int i=min(R,lf);i>=L;i--) if(f[i]>=0) return 1;
return 0;
}
int check(double x)
{
for(int i=n;i>=1;i--)
if(work(tp[i]))
return 1;
return 0;
}
int main()
{
input();
double l=0,r=1e6,mid;
while(l+1e-4<=r)
{
mid=(l+r)/2;
for(int i=1;i<=p;i++) edge[i].w-=mid;
if(check(mid)) l=mid,ans=mid;
else r=mid;
for(int i=1;i<=p;i++) edge[i].w+=mid;
}
printf("%.3lf\n",ans);
return 0;
}