Time:2016.08.21
Author:xiaoyimi
转载注明出处谢谢
传送门
思路:
UPD 2017.1.18
之前的思路删掉了,因为写的太烂,纯属放屁
刚刚知道新加了一组数据,所以原先的那个T掉了……然后又改了好久,把求重心代码更正+预处理还是跑不动,最后发现Rivendell学长早在一年前预示到了这个问题(orz),接着又去学了下01分数规划的一些知识,才AC的
重新说一下思路好了
我们发现题目所求解的东西符合01分数规划的基本形式,然后就可以二分答案,把边减去答案,然后暴力验证是否存在
u,v
,使得
dis(u,v)≥0
且
dep(u)+dep(v)−2×dep(lca(u,v))∈[L,R]
,这样做是
O(n2logn)
;正解考虑点分治,转化成每次重心求解时验证是否存在
u,v
,使得
disu+disv≥0
且
depu+depv∈[L,R]
,这样就可以记录遍历过的子树中深度为
i
的点中
问题看似完美的解决了,但实际上这是放屁,而且聪哥说网上很多博客都是在放屁,今天我刚知道一些点分治的题目要对子树按大小、深度等信息为关键字来排序,不然没法保证正确的复杂度。
比如这道题我们就要对子树的深度排序(我太懒了所以就写的子树大小= =),不然之前遍历到的子树深度可能会很大,在后面统计信息过程中每次都会扫一遍
g
<script type="math/tex" id="MathJax-Element-5598">g</script>数组,导致TLE。
还有一些优化就是把01分数规划的二分改成Dinkelbach算法;把选最优解的过程放到分治过程里面,这样逐层分治子树时答案的下界会越来越大,缩小答案范围,优化时间
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<iostream>
#define M 100004
#define inf 1000000000
#define eps 1e-4
using namespace std;
int root,n,L,R,tot,maxn,cnt;
int first[M],siz[M],mx[M],tmp[M],G[M],S[M];
int Q[M];
struct son{
int v,w,siz;
bool operator <(const son other)const
{
return siz<other.siz;
}
}seq[M];
double f[M],g[M],E,ans,inc;
bool vis[M],flag;
char *cp=(char*)malloc(20000000);
struct edge{
int v,next,w;
}e[M<<1];
struct node{
int P,dep,fa;
double dis;
};
queue<node>q;
inline void in(int &x)
{
for (;*cp<'0'||*cp>'9';++cp);
for (x=0;*cp>='0'&&*cp<='9';++cp) x=x*10+*cp-48;
}
inline void add(int x,int y,int z)
{
e[++tot]=(edge){y,first[x],z};first[x]=tot;
e[++tot]=(edge){x,first[y],z};first[y]=tot;
}
void findroot(int x,int fa)
{
tmp[++cnt]=x;
siz[x]=1;
mx[x]=0;
for (int i=first[x];i;i=e[i].next)
if (!vis[e[i].v]&&fa!=e[i].v)
findroot(e[i].v,x),
siz[x]+=siz[e[i].v],
mx[x]=max(mx[x],siz[e[i].v]);
}
inline void getdis(node now,double num)
{
q.push(now);
maxn=1;
f[1]=max(f[1],now.dis);
for (;!q.empty();q.pop())
{
now=q.front();
for (int i=first[now.P];i;i=e[i].next)
if (e[i].v!=now.fa&&!vis[e[i].v])
q.push((node){e[i].v,now.dep+1,now.P,now.dis+e[i].w-num}),
f[now.dep+1]=max(f[now.dep+1],now.dis+e[i].w-num),
maxn=max(maxn,now.dep+1);
}
}
inline void solve(int x,double num)
{
int i,j,head,tail,v,w,tmp;
vis[x]=1;
for (i=1;i<=siz[x];i++) f[i]=g[i]=-inf;
g[0]=0;
for (int i=1;i<=cnt;++i)
{
v=seq[i].v;w=seq[i].w;
getdis((node){v,1,x,w-num},num);
head=1,tail=0,tmp=0;
for (j=maxn;j>=1;--j)
if (f[j]>-inf)
{
while (tmp<=siz[x]&&tmp+j<=R)
{
while (head<=tail&&g[Q[tail]]<=g[tmp]) --tail;
Q[++tail]=tmp++;
}
while (head<=tail&&Q[head]+j<L) head++;
if (head<=tail&&f[j]+g[Q[head]]>=0) inc=max(inc,(f[j]+g[Q[head]])/(j+Q[head]));
}
for (j=1;j<=maxn;++j)
g[j]=max(g[j],f[j]),
f[j]=-inf;
}
}
inline void cal(int x)
{
root=0;
cnt=0;
findroot(x,0);
if (siz[x]<=L) return;
for (int i=1;i<=cnt;++i)
{
x=tmp[i];
mx[x]=max(mx[x],cnt-siz[x]);
if (!root||mx[x]<mx[root]) root=x;
}
cnt=0;
findroot(root,0);
cnt=0;
for (int j=first[root];j;j=e[j].next)
if (!vis[e[j].v]) seq[++cnt]=(son){e[j].v,e[j].w,siz[e[j].v]};
sort(seq+1,seq+cnt+1);
vis[root]=1;
double begin,end=E,mid;
for (;;)
{
begin=ans;inc=0;
solve(root,begin);
if (inc<=eps) break;
ans+=inc;
}
for (int i=first[root];i;i=e[i].next)
if (!vis[e[i].v]) cal(e[i].v);
}
main()
{
fread(cp,1,20000000,stdin);
in(n);in(L);in(R);
int x,y,z;
for (int i=1;i<n;++i)
in(x),in(y),in(z),
E=max(E,(double)z),
add(x,y,z);
cal(1);
printf("%.3lf",ans);
}