Description
给你一棵n个点的树,你需要在树上选择恰好m条点不相交的,点数至少为k的路径,使得路径所覆盖的点权和尽可能大。求最大点权和。数据保证有解。
n<=1.5e5
凸优化+长链剖分 时间复杂度O(nlogval)
存档题
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fd(i,b,a) for(int i=(b);i>=(a);--i)
#define bfo(i,v,u) for(int i=BB[v],u=B[i][1];i;u=B[i=B[i][0]][1])
#define mset(a,x) memset(a,x,sizeof(a))
#define mcpy(a,b) memcpy(a,b,sizeof(b))
template<typename T> bool chkmin(T &a,const T &b) {return b<a?a=b,1:0;}
template<typename T> bool chkmax(T &a,const T &b) {return b>a?a=b,1:0;}
using namespace std;
typedef long long ll;
int read(){int n=0,p=1;char ch;for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';return n*p;}
const int N=3e5+5;
const ll INF=1e12;
int n,m,k,now,cson[N],st[N],len[N],B0,BB[N],B[N<<1][2];
void link(int u,int v){B[++B0][1]=v,B[B0][0]=BB[u],BB[u]=B0;}
void dfs1(int v,int fr=0)
{
len[v]=1;
bfo(i,v,u) if(u!=fr)
{
dfs1(u,v);
if(chkmax(len[v],len[u]+1)) cson[v]=u;
}
}
void dfs2(int v,int fr=0)
{
st[v]=++now;//g[v][j]==g[st[v]+j]
if(cson[v]) dfs2(cson[v],v);
++now;
bfo(i,v,u) if(u!=fr && u!=cson[v])
dfs2(u,v);
}
ll a[N];
struct info
{
ll x;int y;
friend info operator + (const info &a,const info &b) {return (info){a.x+b.x,a.y+b.y};}
friend info operator - (const info &a,const info &b) {return (info){a.x-b.x,a.y-b.y};}
friend info operator - (const info &a) {return (info){-a.x,-a.y};}
friend bool operator > (const info &a,const info &b) {return a.x!=b.x ? a.x>b.x : a.y<b.y;}
friend bool operator < (const info &a,const info &b) {return !(a > b);}
}s[N],f[N],g[N],tag[N];
void dp(int v,int fr,ll z)
{
bfo(i,v,u) if(u!=fr)
{
dp(u,v,z);
s[v] = s[v] + f[u];
}
f[v] = max(f[v], s[v]);
info d = s[v] + (info){a[v],0};
tag[v] = tag[cson[v]] + d;
g[st[v]] = max(tag[v]+g[st[v]+1], d) - tag[v];
if(len[v]>=k) f[v] = max(f[v], tag[v] + g[st[v]+k-1] + (info){z,1});
bfo(i,v,u) if(u!=fr && u!=cson[v])
{
info t;
fo(j,1,len[u])
{
t = tag[u] + g[st[u]+j-1] + tag[v] + g[st[v]+max(k-j,1)-1];
t = t + (info){z,1};
if(k-j <= len[v]) chkmax(f[v], t);
}
t = (info){-INF,0};
fd(j,len[u],1)
{
chkmax(t, tag[u] + g[st[u]+j-1] + d);
if(t > tag[v] + g[st[v]+j]) g[st[v]+j] = t - tag[v];
}
if(t > tag[v] + g[st[v]]) g[st[v]] = t - tag[v];
}
tag[v] = tag[v] - f[v];
}
void check(ll z)
{
fo(i,1,n)
s[i] = f[i] = g[i] = tag[i] = (info){0,0};
dp(1,0,z);
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
int x,y;
n=read(),m=read(),k=read();
fo(i,1,n) a[i]=read();
fo(i,1,n-1) x=read(),y=read(),link(x,y),link(y,x);
dfs1(1);
dfs2(1);
ll l=-INF,r=INF+1;
while(l<r-1)
{
ll mid=(l+r)>>1;
check(mid);
if(f[1].y==m)
{
printf("%lld\n",f[1].x-m*mid);
return 0;
}
if(f[1].y<m) l=mid;
else r=mid;
}
check(l);
printf("%lld\n",f[1].x-m*l);
return 0;
}