解题思路:
1.对于这个
A
v
g
v
a
l
u
e
=
∑
e
∈
s
v
(
e
)
∣
s
∣
Avgvalue = \frac{\sum_{e\in s}v(e)}{|s|}
Avgvalue=∣s∣∑e∈sv(e) 求最大值,我们知道着很明显是一个01分数规划的式子,对于01分数规划问题我们可以通过二分,把求值问题转化为判定问题。
A
v
g
v
a
l
u
e
=
∑
e
∈
s
v
(
e
)
∣
s
∣
>
=
m
i
d
Avgvalue = \frac{\sum_{e\in s}v(e)}{|s|} >= mid
Avgvalue=∣s∣∑e∈sv(e)>=mid
A
v
g
v
a
l
u
e
=
∑
e
∈
s
v
(
e
)
>
=
∣
s
∣
∗
m
i
d
Avgvalue = \sum_{e\in s}v(e) >= |s| * mid
Avgvalue=e∈s∑v(e)>=∣s∣∗mid
A
v
g
v
a
l
u
e
=
∑
e
∈
s
v
(
e
)
−
∣
s
∣
∗
m
i
d
>
=
0
Avgvalue = \sum_{e\in s}v(e) - |s| * mid>=0
Avgvalue=e∈s∑v(e)−∣s∣∗mid>=0
那么问题就变成了对于一个
m
i
d
mid
mid,我们要求是否存在一条路径(路径权值都减去
m
i
d
mid
mid)这条路径的权值和大于或者等于0
2.那么对于上面的问题可以用点分治求解,但是有一个问题就是路径的长度要限制在 [ L , R ] [L,R] [L,R]之间。
我们最朴实的思想就是分治每一个点求出各个子树中各个深度节点的路径权值和的最大值。如何维护 [ L , R ] [L,R] [L,R]之间的最大值呢?
我们这么看对于深度为1的点就是在 [ L − 1 , R − 1 ] [L-1,R-1] [L−1,R−1]之间找,对于深度为2的点就是在 [ L − 2 , R − 2 ] [L-2,R-2] [L−2,R−2]之间找,那么就有点像滑动窗口了,就可以用单调队列来维护固定区间最大值。
通过上面的思路,那么对于深度相同的点要放到一起,就是 b f s bfs bfs嘛!!
我们先bfs把整个子树的各个点的深度距离全都处理出来再跑单调队列,如果边跑边单调队列会 M L E MLE MLE因为要记录的东西太多了!!
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5 + 10;
const int N = 1e5 + 10;
const double eps = 1e-6;
typedef pair<double,int> PII;
struct node {
int nxt, to;
double cost;
}edge[maxn];
int G[N], idx;
int head[N], cnt;
int n, L, R;
double mid;
inline void add(int from, int to, double w) {
edge[cnt] = {head[from],to,w};
head[from] = cnt ++;
}
bool vis[N];
int max_son[N], siz[N], rt, MX = 1e9, max_node;
void getroot(int u, int fa) {
max_son[u] = 0, siz[u] = 1;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if(v == fa || vis[v]) continue;
getroot(v,u);
max_son[u] = max(max_son[u],siz[v]);
siz[u] = siz[u] + siz[v];
}
max_son[u] = max(max_son[u],max_node - siz[u]);
if(max_son[u] <= MX) MX = max_son[u], rt = u;
}
void rebuild(int u) {
vis[u] = 1;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if(vis[v]) continue;
MX = 1e9, rt = 0;
max_node = siz[v];
getroot(v,u);
G[idx++] = rt;
rebuild(rt);
}
}
int last, top;
int q[maxn], dep[maxn];
double dist[maxn], len[maxn];
bool visq[maxn];
inline void bfs(int u, double w) {
last = 1, top = 0;
q[++top] = u;
dist[u] = w;
dep[u] = 1;
for(int i = last; i <= top; ++ i) {
int t = q[i];
if(visq[t]) continue;
visq[t] = 1;
for(int i = head[t]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if(vis[v] || visq[v]) continue;
q[++top] = v;
dist[v] = dist[t] + edge[i].cost;
dep[v] = dep[t] + 1;
}
}
for(int i = last; i <= top; ++ i) visq[q[i]] = false;
}
int stk[maxn << 1];
int MXD;
//stk[i] 单调队列里面第i个数是 str[i] 表示长度
inline bool check(int u) {
// bfs队列 tip 队头,top队尾
// 单调队列 t 是队列的头,w是队列的尾巴 q是队列
int t = 1, w = 0, tip = last;
for (int i = min(R, dep[q[top]]); i >= 0; --i)//枚举限定区间
{
int tl = i >= L ? 0 : L - i, tr = R - i;//判断该深度的限定范围 [tl,tr] = [L - i, R - i]
while (t <= w && dep[stk[t]] < tl) ++t;//如果队列存在元素,并且队头的长度 已经小于 L - i了踢出单调队列
while (tip <= top && dep[q[tip]] < tl) ++tip; //如果队头元素小于 L - i 了踢出bfs队列
while (tip <= top && dep[q[tip]] <= tr)//限制len[q[tip]] >= L - i, 了
{
while (t <= w && dist[stk[w]] + eps <= dist[q[tip]]) -- w; //如果单调队列的尾巴元素比要新加的还小,就踢出去,维护一单调递减的
stk[++ w] = q[tip ++];//入队
}
if (t <= w && len[i] + dist[stk[t]] >= -eps)
return true;
}
return false;
}
void dfs(int u, int fa, double val, int dep) {//更新答案
len[dep] = max(len[dep],val);
MXD = max(MXD,dep);
if(dep > R) return;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if(v == fa || vis[v]) continue;
dfs(v,u,val+edge[i].cost,dep+1);
}
}
bool Div(int u) {
for(int i = 0; i <= MXD; ++ i) len[i] = -1e9;
MXD = 0;
vis[u] = 1;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if(vis[v]) continue;
bfs(v,edge[i].cost);
if(check(v)) return true;
for(int j = last; j <= top; ++ j)
len[dep[q[j]]] = max(len[dep[q[j]]],dist[q[j]]);
MXD = max(MXD,dep[q[top]]);
}
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if(vis[v]) continue;
if(Div(G[idx++])) return true;
}
return false;
}
inline bool Tmp(double mid) {
for(int i = 1; i <= n; ++ i)
for(int j = head[i]; ~j; j = edge[j].nxt) {
edge[j].cost -= mid;
}
bool f = Div(G[idx++]);
for(int i = 1; i <= n; ++ i)
for(int j = head[i]; ~j; j = edge[j].nxt) {
edge[j].cost += mid;
}
return f;
}
int main() {
memset(head,-1,sizeof(head));
//............................
scanf("%d",&n);
for(int i = 0; i <= n; ++ i) len[i] = -1e9;
scanf("%d%d",&L,&R);
for(int i = 1; i < n; ++ i) {
int u, v;
double val;
scanf("%d%d%lf",&u,&v,&val);
add(u,v,val);
add(v,u,val);
}
max_node = n, MX = 1e9;
getroot(1,0);
G[idx ++] = rt;
rebuild(rt);
double l = 0.0, r = 1e6;
while(r - l > 1e-5) {
memset(vis,0,sizeof(vis));
idx = 0;
mid = (l + r) / 2.0;
if(Tmp(mid)) l = mid;
else r = mid;
}
printf("%.3f",l);
return 0;
}
/*
6
3 3
1 2 2
2 4 5
2 5 10
1 3 4
3 6 3
5
2 4
1 2 1
2 3 2
3 4 3
4 5 4
*/