题解 P3714 [BJOI2017]树的难题
洛谷题面传送门
咦?鸽子 tzc 竟然来补题解了?incredible(
首先看到这样类似于路径统计的问题我们可以非常自然地想到点分治。每次我们找出每个连通块的重心
时间复杂度
最后稍微总结一下这类点分治解决树上路径计数题目的解题技巧:首先要考虑怎样合并两段路径,如果不好合并那一般使用点分治不太好解决,其次要思考如何维护两段路径的决策,比较简单的使用一个桶即可维护,比较复杂的需用 BIT/线段树/平衡树解决。对于计数问题,容斥也是一个不错的选择,即先不考虑两个端点不在同一子树这一条件,先一股脑把贡献全加上去,再扣掉在同一子树内的情况。
const int MAXN=2e5;
const int INF=0x3f3f3f3f;
const ll INFll=0x3f3f3f3f3f3f3f3fll;
int n,m,L,R,c[MAXN+5],hd[MAXN+5],to[MAXN*2+5],val[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
int mx[MAXN+5],cent=0,siz[MAXN+5];bool vis[MAXN+5];
void findcent(int x,int f,int tot){
mx[x]=0;siz[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
findcent(y,x,tot);siz[x]+=siz[y];
chkmax(mx[x],siz[y]);
} chkmax(mx[x],tot-siz[x]);
if(mx[cent]>mx[x]) cent=x;
}
int dep[MAXN+5];ll sum[MAXN+5];
void getdep(int x,int f,int pre){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f||vis[y]) continue;
dep[y]=dep[x]+1;sum[y]=sum[x]+((z==pre)?0:c[z]);
getdep(y,x,z);
}
}
struct segtree{
struct node{int l,r;ll mx;} s[MAXN*4+5];
stack<int> stk;
void pushup(int k){s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);stk.push(k);}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].mx=-INFll;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int p,ll v){
if(s[k].l==s[k].r) return chkmax(s[k].mx,v),stk.push(k),void();
int mid=s[k].l+s[k].r>>1;(p<=mid)?modify(k<<1,p,v):modify(k<<1|1,p,v);
pushup(k);
}
ll query(int k,int l,int r){
if(l>r) return -INFll;
if(l<=s[k].l&&s[k].r<=r) return s[k].mx;
int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
void relax(){
while(!stk.empty()){
int k=stk.top();stk.pop();
s[k].mx=-INFll;
}
}
} s1,s2;
vector<int> pt;
ll res=-INFll;
void findpts(int x,int f){
pt.pb(x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
findpts(y,x);
}
}
void divcent(int x){
// printf("divcent %d\n",x);
vis[x]=1;dep[x]=sum[x]=0;
vector<pii> sub;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(vis[y]) continue;
dep[y]=1;sum[y]=c[z];getdep(y,x,z);
sub.pb(mp(z,y));
} sort(sub.begin(),sub.end());
vector<int> wt;s1.modify(1,0,0);
for(int i=0;i<sub.size();i++){
if(i&&sub[i].fi!=sub[i-1].fi){
s2.relax();
for(int y:wt) s1.modify(1,dep[y],sum[y]);
wt.clear();
} int y=sub[i].se;pt.clear();findpts(y,x);
for(int z:pt){
int d=dep[z];
if(d<=R){
chkmax(res,s1.query(1,max(L-d,0),R-d)+sum[z]);
chkmax(res,s2.query(1,max(L-d,0),R-d)+sum[z]-c[sub[i].fi]);
}
} for(int z:pt) wt.pb(z),s2.modify(1,dep[z],sum[z]);
// for(int z:pt) printf("%d %d %lld\n",z,dep[z],sum[z]);
} s1.relax();s2.relax();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(vis[y]) continue;cent=0;
findcent(y,x,siz[y]);divcent(cent);
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&L,&R);s1.build(1,0,n);s2.build(1,0,n);
for(int i=1;i<=m;i++) scanf("%d",&c[i]);
for(int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),adde(u,v,w),adde(v,u,w);
mx[0]=INF;findcent(1,0,n);divcent(cent);
printf("%lld\n",res);
return 0;
}