重建计划
题解
看到这道题首先应该是很容易想到0/1分数规划的。毕竟那里明摆着一个
∑
i
=
1
k
−
1
v
a
l
i
∣
S
∣
\frac{\sum_{i=1}^{k-1}val_{i}}{|S|}
∣S∣∑i=1k−1vali。
我们只需要将下面的分母当做
1
1
1即可,所以当二分到
m
i
d
mid
mid时,若
m
i
d
mid
mid可呗构造出来,有
∑
i
=
1
k
−
1
v
a
l
i
∣
S
∣
≥
m
i
d
⟺
∑
i
=
1
k
−
1
(
v
a
l
i
−
m
i
d
)
≥
0
\frac{\sum_{i=1}^{k-1}val_{i}}{|S|}\geq mid\Longleftrightarrow \sum_{i=1}^{k-1}(val_{i}-mid)\geq 0
∣S∣∑i=1k−1vali≥mid⟺∑i=1k−1(vali−mid)≥0。
所以我们只需判断当前的
m
i
d
mid
mid跑出来的有无权值大于0的合法路径即可。
关键是怎么找这个路径。
由于它求的是任意点对
(
u
,
v
)
(u,v)
(u,v)的路径长度最大值,我们很快就想到了点分治。
我们可以通过点分治来找到这些路径的最大长度。
在一个点的子树里统计的时候,我们就先对它的每个儿子的子树跑bfs,找到它的儿子在
[
0
,
U
)
[0,U)
[0,U)的边数内,每种边数的最深距离,再像树dp一样进行合并,用
f
i
f_{i}
fi表示深度为
i
i
i时的最长路径。
合并之前将两个未合并的部分与新求出来的部分一起统计答案,每个
f
f
f对应的区间的
b
f
s
bfs
bfs序一定是连续的,我们可以用单调队列来求出最大值,这个过程时间复杂度是
O
(
子树大小
)
O\left(子树大小\right)
O(子树大小)的。
由于点分治最多只会有
l
o
g
n
log_{n}
logn层,所以每次判读时间复杂度是
O
(
n
l
o
g
n
)
O\left(nlog\,n\right)
O(nlogn)的。
由于点分治的过程是一直沿着重心下去的,我们可以先预处理重心的顺序,这样分治每次判断时就不用单独处理了。
总时间复杂度 O ( n l o g 2 n ) O\left(nlog^2\,n\right) O(nlog2n)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,L,U,head[MAXN],tot,mxson[MAXN],siz[MAXN],S,mx;
int root[MAXN],idx,sta[MAXN],stak,las,len[MAXN],q[MAXN];
double dis[MAXN],f[MAXN];bool vis[MAXN],vp[MAXN],flag;
struct ming{int u,v,w;}a[MAXN];
struct edge{int to,nxt;double paid;}e[MAXN<<1];
void addEdge(int u,int v,double w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
void bfs(int st){
vp[sta[++stak]=st]=1;
for(int i=las+1;i<=stak;i++)
for(int j=head[sta[i]];j;j=e[j].nxt){
int v=e[j].to;if(vis[v]||vp[v])continue;
dis[v]=dis[sta[i]]+e[j].paid;
len[v]=len[sta[i]]+1;vp[sta[++stak]=v]=1;
}
for(int i=las+1;i<=stak;i++)vp[sta[i]]=0;
}
void check(){
int head=1,tail=0,id=las+1;
for(int i=min(U,len[sta[stak]]);i>=0;i--){
int tl=max(L-i,0),tr=U-i;
while(head<=tail&&len[q[head]]<tl)head++;
while(id<=stak&&len[sta[id]]<tl)++id;
while(id<=stak&&len[sta[id]]<=tr){
while(head<=tail&&dis[q[tail]]<=dis[sta[id]])tail--;
q[++tail]=sta[id++];
}
if(head<=tail&&f[i]+dis[q[head]]>=0)return (void)(flag=1);
}
}
void devide(int x){
vis[x]=1;f[0]=dis[x]=len[x]=0;sta[++stak]=x;
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;if(vis[v])continue;
las=stak;dis[v]=e[i].paid;len[v]=1;bfs(v);check();
for(int j=las+1;j<=stak;j++)
f[len[sta[j]]]=max(f[len[sta[j]]],dis[sta[j]]);
}
while(stak)f[len[sta[stak--]]]=-INF;
for(int i=head[x];i;i=e[i].nxt)if(!vis[e[i].to])devide(root[++idx]);
}
bool sakura(double mid){
idx=tot=0;for(int i=1;i<=n;i++)head[i]=vis[i]=0;
for(int i=1;i<n;i++)
addEdge(a[i].u,a[i].v,1.0*a[i].w-mid),
addEdge(a[i].v,a[i].u,1.0*a[i].w-mid);
flag=0;devide(root[++idx]);return flag;
}
void getroot(int u,int fa){
siz[u]=1;mxson[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa||vis[v])continue;getroot(v,u);
siz[u]+=siz[v];mxson[u]=max(siz[v],mxson[u]);
}
mxson[u]=max(mxson[u],S-siz[u]);
if(mxson[u]<mx){mx=mxson[u];root[idx]=u;}
}
void dfs(int u){
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(vis[v])continue;
S=siz[v];++idx;mx=INF;getroot(v,0);dfs(root[idx]);
}
}
void init(){
tot=0;for(int i=1;i<=n;i++)head[i]=0;
for(int i=1;i<n;i++)addEdge(a[i].u,a[i].v,0),addEdge(a[i].v,a[i].u,0);
mx=INF;S=n;++idx;getroot(1,0);dfs(root[idx]);
}
signed main(){
read(n);read(L);read(U);
for(int i=1;i<n;i++)read(a[i].u),read(a[i].v),read(a[i].w);
for(int i=0;i<n;i++)f[i]=-INF;
init();double l=0,r=1e6,times=40,Ans=r;
while(times--){
double mid=(l+r)/2.0;
if(sakura(mid))l=mid,Ans=mid;
else r=mid;
}
printf("%.3f\n",Ans);
return 0;
}