Luogu P4292 [WC2010]重建计划
\[Ans\leq \frac{\sum_{e\in S}v(e)}{|S|}\\
\therefore \sum_{e\in S} (v(e)-Ans)\geq0
\]
然后找边权最大的点对,判断是否大于0
法1:长链剖分+线段树
考虑最简单的DP,\(f[i][j]\)表示\(i\)号点深度为\(j\)的最大和,和深度有关,可以用长链剖分
考虑如何维护边权和
当轻链合并到重链上时,时间复杂度只能与轻链长度有关
要求区间最大值,可以用线段树维护,一定要先求最大值,再进行修改
另外还需要区间加,单点改,直接在线段树上维护重链信息,千万不能新开数组,区间加没法做(会导致时间复杂度和重链长度挂钩)
#include<bits/stdc++.h>
#define ll long long
const int fj=1e5;
const ll INF=1e18;
using namespace std;
const int N=1e5+5,M=1e6+5;
int n,L,R,dep[N],E1[N],E2[N],E3[N],dfn[N],son[N];
ll c[M],t[M],ret;
vector<int>V1[N];
struct A{int v; ll w;};
vector<A>V[N];
inline void up(int p) {
c[p]=max(c[p<<1],c[p<<1|1]);
}
inline void down(int p) {
if(t[p]) {
t[p<<1]+=t[p],c[p<<1]+=t[p];
t[p<<1|1]+=t[p],c[p<<1|1]+=t[p];
t[p]=0;
}
}
void upd(int p,int l,int r,int x,ll k) {
if(l==r) {
c[p]=k; return;
}
down(p);
int mid=l+r>>1;
if(x<=mid) upd(p<<1,l,mid,x,k);
else upd(p<<1|1,mid+1,r,x,k);
up(p);
}
void add(int p,int l,int r,int x,int y,ll k) {
if(l==x&&r==y) {
c[p]+=k,t[p]+=k;
return;
}
down(p);
int mid=l+r>>1;
if(y<=mid) add(p<<1,l,mid,x,y,k);
else if(x>mid) add(p<<1|1,mid+1,r,x,y,k);
else {
add(p<<1,l,mid,x,mid,k);
add(p<<1|1,mid+1,r,mid+1,y,k);
}
up(p);
}
ll sum(int p,int l,int r,int x,int y) {
if(x>y) return -INF;
if(l==x&&r==y) return c[p];
down(p);
int mid=l+r>>1;
if(y<=mid) return sum(p<<1,l,mid,x,y);
if(x>mid) return sum(p<<1|1,mid+1,r,x,y);
return max(sum(p<<1,l,mid,x,mid),sum(p<<1|1,mid+1,r,mid+1,y));
}
void dhs(int fa,int u) {
upd(1,1,n,dfn[u],0);
for(A v:V[u]) {
if(v.v==son[u]) {
dhs(u,son[u]);
add(1,1,n,dfn[u]+1,dfn[u]+dep[u]-1,v.w);
ret=max(ret,sum(1,1,n,max(dfn[u]+L-1,dfn[u]),min(dfn[u]+R-1,dfn[u]+dep[u]-1)));
}
}
for(A v:V[u]) {
if(v.v!=son[u]&&v.v!=fa) {
dhs(u,v.v);
for(int i=1;i<=dep[v.v];i++) {
ret=max(ret,sum(1,1,n,max(dfn[u]+L-i-1,dfn[u]),min(dfn[u]+R-i-1,dfn[u]+dep[u]-1))+sum(1,1,n,dfn[v.v]+i-1,dfn[v.v]+i-1)+v.w);
}
for(int i=1;i<=dep[v.v];i++) {
ll t=sum(1,1,n,dfn[v.v]+i-1,dfn[v.v]+i-1);
if(sum(1,1,n,dfn[u]+i,dfn[u]+i)<t+v.w) {
upd(1,1,n,dfn[u]+i,t+v.w);
}
}
}
}
}
void bld(int p,int l,int r) {
t[p]=0,c[p]=-INF;
if(l==r) return;
int mid=l+r>>1;
bld(p<<1,l,mid),bld(p<<1|1,mid+1,r);
}
bool check(ll mid) {
for(int i=1;i<=n;i++) {
V[i].clear();
vector<A>().swap(V[i]);
}
for(int i=1;i<n;i++) {
int u=E1[i],v=E2[i],k=E3[i];
V[u].push_back((A){v,(ll)k*fj-mid});
V[v].push_back((A){u,(ll)k*fj-mid});
}
bld(1,1,n);
ret=-INF;
dhs(0,1);
return ret>=0;
}
void dfs(int fa,int u) {
dep[u]=1;
for(int v:V1[u]) {
if(v!=fa) {
dfs(u,v);
dep[u]=max(dep[u],dep[v]+1);
if(dep[son[u]]<dep[v]) son[u]=v;
}
}
}
int sgn;
void dgs(int fa,int u) {
dfn[u]=++sgn;
if(son[u]) dgs(u,son[u]);
for(int v:V1[u]) {
if(v!=fa&&v!=son[u]) {
dgs(u,v);
}
}
}
int main(){
scanf("%d%d%d",&n,&L,&R); L++,R++;
for(int i=1;i<n;i++) {
scanf("%d%d%d",&E1[i],&E2[i],&E3[i]);
V1[E1[i]].push_back(E2[i]);
V1[E2[i]].push_back(E1[i]);
}
dfs(0,1),dgs(0,1);
ll l=0,r=1e11,mid,ans;
while(l<=r) {
mid=l+r>>1;
if(check(mid)) {
ans=mid,l=mid+1;
} else r=mid-1;
}
printf("%.3lf\n",(double)ans/fj);
return 0;
}
法2
点分治+单调队列
点分治后暴力求出深度数组,合并可以按深度排序后采用单调队列(滑动窗口)

长链剖分,点分治
浙公网安备 33010602011771号