题解 P4383 【[八省联考2018]林克卡特树lct】
EternalAlexander · · 题解
emm感觉其他题解都没有讲的特别清楚。写一篇讲的比较详细的吧。
带权二分,又叫wqs二分/dp凸优化,用于解决一类有固定数量限制的最优化问题。
例如,给你
因为有了
但是,带权二分能够成为这类问题的一个突破口。令
具体而言,对于
一个凸包的示例
我们要求的是
考虑引一条固定斜率的直线去切这个凸包,也就是从上向下平移找到最早相交的那个点,这个切点的坐标是可以求出来的(将在后文详细叙述)。也就是说我们能够确定凸包上的一个点
引一条直线切凸包
如果
这样,我们可以通过二分直线斜率的方式来求得
黄绿红三条直线斜率依次减小,所得切点横坐标依次增大
到了现在,我们唯一需要解决的问题就是怎么找到切点坐标。
设该直线斜率为
在图像上,我们可以将凸包上的每个点
设切点坐标为
在切到了
对应到给定的题目中,只需要把每个物品的收益减去二分的斜率
回到本题,显然题目可以转化为选取
稍有不同的是,我们这里需要找到的是最小的可以使得选取的路径条数
至于如何求出选取
另外还有一些边界和细节问题,这里就不在赘述了,可以参考其他题解或者理解后自行推导。
#include <bits/stdc++.h>
#define maxn 300005
#define ll long long
const int inf=1e13;
struct edge {
int v, w, next;
}edges[maxn<<1];
int n, k, tail=0, u, v, w, cnt, head[maxn]={0};
ll sum,delta,l,r=0,ans;
void add_edge(int u, int v, int w) {
edges[++tail].v=v;
edges[tail].w=w;
edges[tail].next=head[u];
head[u]=tail;
}
struct data{
int c; ll v;
data operator + (data d) {data a;a.v=v+d.v;a.c=c+d.c;return a;}
bool operator < (data d) {return v<d.v||(v==d.v&&c<d.c);}
}dp[maxn][3], temp[3];
data dat(ll v, int c) {data d;d.v=v;d.c=c;return d;}
data max(data a, data b) {
if (a<b) return b;
return a;
}
void dfs(int u, int f) {
for (int i=head[u];i;i=edges[i].next) {
int v=edges[i].v;
if (v==f) continue;
dfs(v, u);
dp[u][2]=max(dp[u][2]+dp[v][0], dp[u][1]+dp[v][1]+dat(edges[i].w-delta, 1));
dp[u][1]=max(dp[u][0]+dp[v][1]+dat(edges[i].w, 0), dp[u][1]+dp[v][0]);
dp[u][0]=dp[u][0]+dp[v][0];
}dp[u][0]=max(dp[u][0], max(dp[u][1]+dat(-delta, 1), dp[u][2]));
}
void check(ll x) {
delta=x;
for (int i=1;i<=n;++i) {
dp[i][0]=dat(0, 0);dp[i][2]=dat(-delta, 1); dp[i][1]=dat(0, 0);
} dfs(1, 0);
sum=dp[1][0].v; cnt=dp[1][0].c;
}
int main() {
scanf("%d %d", &n, &k);k++;
for (int i=1;i<n;++i) {
scanf("%d %d %d",&u,&v,&w);
add_edge(u,v,w);add_edge(v,u,w);
r+=(w>0)?w:-w;
} l=-r;
while (l<=r) {
ll mid=(l+r)>>1;
check(mid);
if (cnt>=k) {l=mid+1; ans=mid;}
else r=mid-1;
}check(ans);
printf("%lld", sum+k*ans);
return 0;
}
wqs的论文:http://www.doc88.com/p-949564862405.html
最后,关于本题