隐藏
「WC2010」重建计划 - 分数规划+点分治/长链剖分 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「WC2010」重建计划 - 分数规划+点分治/长链剖分

题目大意


题目分析

首先对于题目的分数形式答案,不难想到分数规划。
二分答案,减少边权进行验证。
现在需要考虑的是,如何统计边数在$[L,U]$间的最大路径长。

不难想到使用点分治进行维护。
用单调队列合并信息,可以做到$O(n\log n\log v)$的时间复杂度。

然而我们也可以使用长链剖分的方法来完成。
不妨建立一个长度为$n$的答案表,顺序是长链剖分的Dfs序。
对于一条长链,可以共用答案表中的状态,而对于轻儿子,需要暴力合并到父亲的长链中。
因为统计区间,需要用到线段树,故时间复杂度仍为$O(n\log n\log v)$。

因为线段树大常数,故比点分慢一些。
因为我懒,所以不想写点分了。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int maxn=100005;
const double eps=1e-4;

double max(double a,double b) {
return a>b?a:b;
}

int id[maxn];

struct Segment_Tree {
struct Tree {
int left,right;
double max;
Tree(int l=0,int r=0,double m=-1e18):left(l),right(r),max(m) {}
} tree[maxn<<2];
#define ls index<<1
#define rs index<<1|1
void build(int index,int Left,int Right) {
tree[index]=Tree(Left,Right);
if(Left==Right) {
id[Left]=index;
return;
}
int mid=(Left+Right)>>1;
build(ls,Left,mid);
build(rs,mid+1,Right);
}
void refresh(int Now,double v) {
Now=id[Now];
while(Now) {
tree[Now].max=max(tree[Now].max,v);
Now>>=1;
}
}
double query(int index,int Left,int Right) {
if(Right<tree[index].left||Left>tree[index].right)return -1e18;
if(Left<=tree[index].left&&Right>=tree[index].right)return tree[index].max;
return max(query(ls,Left,Right),query(rs,Left,Right));
}
} st;

struct Edge {
int from,to;
double dist;
};

int n,L,R,step=0,Depth[maxn],Maxdep[maxn],Son[maxn],father[maxn],Top[maxn],Dfn[maxn];
double Sondist[maxn],tmp[maxn];
vector<Edge> edges[maxn];

void AddEdge(int x,int y,double v) {
edges[x].push_back((Edge) {x,y,v});
}

void Dfs1(int Now,int fa,int depth) {
Depth[Now]=Maxdep[Now]=depth;
father[Now]=fa;
for(Edge& e:edges[Now]) {
int Next=e.to;
if(Next==fa)continue;
Dfs1(Next,Now,depth+1);
if(Maxdep[Next]>Maxdep[Son[Now]]) {
Son[Now]=Next;
Sondist[Now]=e.dist;
Maxdep[Now]=Maxdep[Next];
}
}
}

void Dfs2(int Now,int top) {
Top[Now]=top;
Dfn[Now]=++step;
if(Son[Now])Dfs2(Son[Now],top);
for(Edge& e:edges[Now]) {
int Next=e.to;
if(Next==father[Now]||Next==Son[Now])continue;
Dfs2(Next,Next);
}
}

double Ask(int Now,int Left,int Right) {
Left=max(Left,0);
Right=min(Right,Maxdep[Now]-Depth[Now]);
if(Left>Right)return -1e18;
return st.query(1,Dfn[Now]+Left,Dfn[Now]+Right);
}

double ans=1e-8;

void TreeDp(int Now,double dist,double mid) {
st.refresh(Dfn[Now],dist);
if(Son[Now])TreeDp(Son[Now],dist+Sondist[Now]-mid,mid);
for(Edge& e:edges[Now]) {
int Next=e.to;
if(Next==father[Now]||Next==Son[Now])continue;
TreeDp(Next,dist+e.dist-mid,mid);
for(int j=1; j<=Maxdep[Next]-Depth[Next]+1; j++) {
tmp[j]=st.tree[id[Dfn[Next]+j-1]].max;
ans=max(ans,tmp[j]+Ask(Now,L-j,R-j)-2*dist);
}
for(int j=1; j<=Maxdep[Next]-Depth[Next]+1; j++)st.refresh(Dfn[Now]+j,tmp[j]);
}
ans=max(ans,Ask(Now,L,R)-dist);
}

bool Check(double mid) {
st.build(1,1,n);
ans=-1e18;
TreeDp(1,0,mid);
return ans>=-eps;
}

int main() {
int size=40<<20;
__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));
n=Get_Int();
L=Get_Int();
R=Get_Int();
for(int i=1; i<n; i++) {
int x=Get_Int(),y=Get_Int(),v=Get_Int();
AddEdge(x,y,v);
AddEdge(y,x,v);
}
Dfs1(1,0,1);
Dfs2(1,1);
double Left=0,Right=1e10;
while(Right-Left>eps) {
double mid=(Left+Right)/2;
if(Check(mid))Left=mid;
else Right=mid;
}
printf("%0.3lf\n",(Left+Right)/2);
exit(0);
}
姥爷们赏瓶冰阔落吧~