[八省联考 2018]林克卡特树
题意:求一个凸函数的最优解。。。
思路:
好吧在题意里已经说出来了。
对于被卡斜率的,只能\(orz\)了。。。
其实据说还有\(12s\)评测这种操作,对不起我省\(5s\)。
好吧不废话了,看看应该怎么做。
暴力的话直接记\(dp[i][j][0->2]\)表示当前做到第 \(i\) 棵子树用了$ j$ 条链并且当前点有\(0->2\)条出边,转移也好想。。。
正解的话。。。就是二分斜率。。
我们记\(f[i][0->2]\)表示做到$ i $子树并且出边是\(0->2\)个。
然后差分,发现单调凸性质,在函数上二分最优解即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 600010;
inline int read()
{
int q=0,f=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;ch=getchar();
}
while(isdigit(ch)){
q=q*10+ch-'0';ch=getchar();
}
return q*f;
}
struct node
{
int x;int y;
inline void max(int a,int b){
if(x < a||(x == a && y > b)) x = a,y = b;
}
}fp[maxn][3];
node dp[maxn];
node fg[3];
node tmp;
int n,m,k,q;
int cnt;
struct edge
{
int to;
int nxt;
int w;
}e[maxn<<4];
int head[maxn];
inline void add(int u,int v,int w){
e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].w=w;return;
}
const int inf = 0x7fffffff;
inline void dfs(int x,int f){
fp[x][0].x=0;fp[x][0].y=0;
//fp[x][0]=(0,0);
fp[x][1].x=-q;fp[x][1].y=1;
fp[x][2].x=-inf;fp[x][2].y=0;
for(int i = head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y != f){
dfs(y,x);
for(int j = 0;j < 3; ++j){
fg[j]=fp[x][j];
}
tmp = dp[y];
for(int j = 0;j < 3; ++j){
fp[x][j].max(fg[j].x+tmp.x,fg[j].y+tmp.y);
}
fp[x][1].max(fg[0].x+fp[y][1].x+e[i].w,fg[0].y+fp[y][1].y);
fp[x][2].max(fg[1].x+fp[y][1].x+e[i].w+q,fg[1].y+fp[y][1].y-1);
}
}
dp[x]=fp[x][0];
for(int i = 1;i < 3; ++i){
dp[x].max(fp[x][i].x,fp[x][i].y);
}
return;
}
int ans;
signed main()
{
n=read(),m=read();
++m;
for(int i = 1;i < n; ++i){
int u=read(),v=read(),w=read();
add(u,v,w);add(v,u,w);
}
int l=-inf,r=inf;
while(l <= r){
q=(l + r)>>1;
dfs(1,0);
if(dp[1].y <= m){
ans = q;
r = q - 1;
}
else l = q + 1;
}
q = ans;
dfs(1,0);
cout<<dp[1].x+m*q<<endl;
return 0;
}