题意:树上最长不相交k条链。

 #include <cstdio>
#include <algorithm>
#include <cstring> typedef long long LL;
const int N = ; struct Edge {
int nex, v;
LL len;
}edge[N << ]; int top; LL f[N][][];
int e[N], n, k, siz[N]; inline void add(int x, int y, LL z) {
top++;
edge[top].v = y;
edge[top].len = z;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void exmax(LL &a, LL b) {
if(a < b) {
a = b;
}
return;
} void DFS(int x, int fa) {
siz[x] = ;
f[x][][] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == fa) {
continue;
}
DFS(y, x);
siz[x] += siz[y];
// DP
for(int j = std::min(k, siz[x]); j >= ; j--) {
for(int p = ; p <= j && p <= siz[y]; p++) { // p in son
exmax(f[x][j][], f[x][j - p + ][] + f[y][p][] + edge[i].len);
exmax(f[x][j][], f[x][j - p][] + std::max(f[y][p][], std::max(f[y][p][], f[y][p][])));
exmax(f[x][j][], f[x][j - p][] + std::max(f[y][p][], std::max(f[y][p][], f[y][p][])));
exmax(f[x][j][], f[x][j - p][] + std::max(f[y][p][], std::max(f[y][p][], f[y][p][])));
exmax(f[x][j][], f[x][j - p][] + f[y][p][] + edge[i].len);
}
}
}
for(int i = ; i <= k && i <= siz[x]; i++) {
exmax(f[x][i][], f[x][i - ][]);
}
/*printf("x = %d \n", x);
for(int i = 0; i <= k && i <= siz[x]; i++) {
printf("%d || 0 : %lld 1 : %lld 2 : %lld \n", i, f[x][i][0], f[x][i][1], f[x][i][2]);
}*/
return;
} int main() {
int n;
memset(f, ~0x3f, sizeof(f));
scanf("%d%d", &n, &k);
k++;
int x, y;
LL z;
for(int i = ; i < n; i++) {
scanf("%d%d%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
DFS(, );
printf("%lld\n", std::max(std::max(f[][k][], f[][k][]), f[][k][]));
return ;
}

60分DP

解:带权二分/wqs二分/DP凸优化。

一个比较常见的套路吧。

一般是求解有k限制的最优化问题。且随着k的变化,极值函数上凸/下凸。

这时我们二分一个斜率去切它,会有一个斜率切到我们要的k。

感性理解一下,我们给这k个事物附上权值,然后权值增加的时候k就会变多,权值减小(可以为负)的时候k会变少。

然后会有某个权值使得不限制k时的最优值恰好选了k。这时候我们减去附加的权值即可。

本题就是给每条链加上一个权值。

还有一道题是k条白边,剩下的选黑边的最小生成树。

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath> typedef long long LL;
const int N = ;
const LL INF = 1e17; struct Edge {
int nex, v;
LL len;
}edge[N << ]; int top; LL f[N][], D;
int g[N][], n, k, e[N]; inline void add(int x, int y, LL z) {
top++;
edge[top].v = y;
edge[top].len = z;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void exmax(LL &a, int &c, LL b, int d) {
if(a < b || (a == b && c > d)) {
a = b;
c = d;
}
return;
} void DFS(int x, int fa) {
f[x][] = ; // 初始化 不选链
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == fa) {
continue;
}
DFS(y, x);
exmax(f[x][], g[x][], f[x][] + f[y][], g[x][] + g[y][]);
exmax(f[x][], g[x][], f[x][] + f[y][] + edge[i].len - D, g[x][] + g[y][] - ); // 1 -> 2 exmax(f[x][], g[x][], f[x][] + f[y][], g[x][] + g[y][]);
exmax(f[x][], g[x][], f[x][] + f[y][] + edge[i].len, g[x][] + g[y][]); // 0 -> 1 exmax(f[x][], g[x][], f[x][] + f[y][], g[x][] + g[y][]);
}
exmax(f[x][], g[x][], f[x][] + D, g[x][] + ); // 自己单独开链
exmax(f[x][], g[x][], f[x][], g[x][]);
exmax(f[x][], g[x][], f[x][], g[x][]);
return;
} inline bool check(LL mid) {
D = mid;
memset(g, , sizeof(g));
memset(f, ~0x3f, sizeof(f));
DFS(, );
//printf("D = %lld \nf = %lld g = %d \n\n", D, f[1][2], g[1][2]);
return ;
} int main() { scanf("%d%d", &n, &k);
LL z, r = , l;
k++;
for(int i = , x, y; i < n; i++) {
scanf("%d%d%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
r += std::abs(z);
}
l = -r;
while(l < r) {
LL mid = (l + r + ) >> ;
//printf("%lld %lld mid = %lld \n", l, r, mid);
check(mid);
if(g[][] == k) {
printf("%lld\n", f[][] - k * mid);
return ;
}
if(g[][] > k) {
r = mid - ;
}
else {
l = mid;
}
} check(r);
printf("%lld\n", f[][] - k * r);
return ;
}

AC代码

本题只要整数二分就行了。

负数二分用右移,是向下取整。

细节:可能最优点不是凸包上的顶点,是一条边中间。我们这时找到靠左的那个顶点,然后用这个斜率 * k就行了。

实数版:

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath> typedef long long LL;
const int N = ;
const LL INF = 1e17;
const double eps = 1e-; struct Edge {
int nex, v;
LL len;
}edge[N << ]; int top; double f[N][], D;
int g[N][], n, k, e[N]; inline void add(int x, int y, LL z) {
top++;
edge[top].v = y;
edge[top].len = z;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void exmax(double &a, int &c, double b, int d) {
// if(a < b || (a == b && c > d)) {
if(a < b) {
a = b;
c = d;
}
return;
} void DFS(int x, int fa) {
f[x][] = ; // 初始化 不选链
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == fa) {
continue;
}
DFS(y, x);
exmax(f[x][], g[x][], f[x][] + f[y][], g[x][] + g[y][]);
exmax(f[x][], g[x][], f[x][] + f[y][] + edge[i].len - D, g[x][] + g[y][] - ); // 1 -> 2 exmax(f[x][], g[x][], f[x][] + f[y][], g[x][] + g[y][]);
exmax(f[x][], g[x][], f[x][] + f[y][] + edge[i].len, g[x][] + g[y][]); // 0 -> 1 exmax(f[x][], g[x][], f[x][] + f[y][], g[x][] + g[y][]);
}
exmax(f[x][], g[x][], f[x][] + D, g[x][] + ); // 自己单独开链
exmax(f[x][], g[x][], f[x][], g[x][]);
exmax(f[x][], g[x][], f[x][], g[x][]);
return;
} inline bool check(double mid) {
D = mid;
memset(g, , sizeof(g));
//memset(f, ~0x3f, sizeof(f));
for(int i = ; i <= n; i++) {
f[i][] = f[i][] = f[i][] = -INF;
}
DFS(, );
//printf("D = %lld \nf = %lld g = %d \n\n", D, f[1][2], g[1][2]);
return ;
} int main() { scanf("%d%d", &n, &k);
LL z;
double r = , l;
k++;
for(int i = , x, y; i < n; i++) {
scanf("%d%d%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
r += std::abs(z);
}
l = -r;
while(fabs(r - l) > eps) {
double mid = (l + r) / ;
//printf("%lld %lld mid = %lld \n", l, r, mid);
check(mid);
if(g[][] == k) {
printf("%.0f\n", f[][] - k * mid);
return ;
}
if(g[][] > k) {
r = mid;
}
else {
l = mid;
}
} check(r);
printf("%.0f\n", f[][] - k * r);
return ;
}

AC代码

洛谷P4383 林克卡特树的更多相关文章

  1. P4383 [八省联考2018]林克卡特树 树形dp Wqs二分

    LINK:林克卡特树 作为树形dp 这道题已经属于不容易的级别了. 套上了Wqs二分 (反而更简单了 大雾 容易想到还是对树进行联通情况的dp 然后最后结果总和为各个联通块内的直径. \(f_{i,j ...

  2. 【BZOJ5252】林克卡特树(动态规划,凸优化)

    [BZOJ5252]林克卡特树(动态规划,凸优化) 题面 BZOJ(交不了) 洛谷 题解 这个东西显然是随着断开的越来越多,收益增长速度渐渐放慢. 所以可以凸优化. 考虑一个和\(k\)相关的\(dp ...

  3. LuoguP4383 [八省联考2018]林克卡特树lct

    LuoguP4383 [八省联考2018]林克卡特树lct https://www.luogu.org/problemnew/show/P4383 分析: 题意等价于选择\(K\)条点不相交的链,使得 ...

  4. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  5. [八省联考2018]林克卡特树lct——WQS二分

    [八省联考2018]林克卡特树lct 一看这种题就不是lct... 除了直径好拿分,别的都难做. 所以必须转化 突破口在于:连“0”边 对于k=0,我们求直径 k=1,对于(p,q)一定是从p出发,走 ...

  6. [BZOJ 5252][LOJ 2478][九省联考2018] 林克卡特树

    [BZOJ 5252][LOJ 2478][九省联考2018] 林克卡特树 题意 给定一个 \(n\) 个点边带权的无根树, 要求切断其中恰好 \(k\) 条边再连 \(k\) 条边权为 \(0\) ...

  7. 【BZOJ2830/洛谷3830】随机树(动态规划)

    [BZOJ2830/洛谷3830]随机树(动态规划) 题面 洛谷 题解 先考虑第一问. 第一问的答案显然就是所有情况下所有点的深度的平均数. 考虑新加入的两个点,一定会删去某个叶子,然后新加入两个深度 ...

  8. luoguP4383 [八省联考2018]林克卡特树(树上dp,wqs二分)

    luoguP4383 [八省联考2018]林克卡特树(树上dp,wqs二分) Luogu 题解时间 $ k $ 条边权为 $ 0 $ 的边. 是的,边权为零. 转化成选正好 $ k+1 $ 条链. $ ...

  9. 洛谷P3655 差分数组 树状数组

    题目链接:https://www.luogu.org/problemnew/show/P3655 不一定对,仅供参考,不喜勿喷,不喜勿喷. 先copy洛谷P3368 [模板]树状数组 2 题解里面一位 ...

随机推荐

  1. Terraform:简介

    在 DevOps 实践中,基础设施即代码如何落地是一个绕不开的话题.像 Chef,Puppet 等成熟的配置管理工具,都能够满足一定程度的需求,但有没有更友好的工具能够满足我们绝大多数的需求?笔者认为 ...

  2. restfull环境搭建-helloword(二)

    原文地址:http://only81.iteye.com/blog/1689537 本文描述,获取XML或json格式数据 首先,创建一个bean,比如Todo(JAXB自动将bean文件,转换成xm ...

  3. CF1016 D. Vasya And The Matrix

    传送门 [http://codeforces.com/group/1EzrFFyOc0/contest/1016/problem/D] 题意 已知矩阵n行m列,以及每一行,每一列所有元素的异或,用 a ...

  4. 个人实验 github地址:https://github.com/quchengyu/cher

    一.实践目的 1.掌握类的定义,对象的创建. 2.掌握实现封装.继承.多态的方法,掌握各种修饰符的使用. 3.掌握将对象数组作为方法的参数和返回值. 4.掌握抽象类与接口的概念及实现,理解动态绑定机制 ...

  5. 【目标跟踪】相关滤波算法之MOSSE

    简要 2010年David S. Bolme等人在CVPR上发表了<Visual Object Tracking using Adaptive Correlation Filters>一文 ...

  6. 关于singleton的几个实现

    public class Singleton { public static void main(String[] args) { Singleton s1 = Singleton.getInstan ...

  7. FTP Download File By Some Order List

    @Echo Off REM -- Define File Filter, i.e. files with extension .RBSet FindStrArgs=/E /C:".asp&q ...

  8. 数据驱动测试之—— Excel+TestNG

    对于利用Webdriver做自动化的童鞋,对于如何将元素或者输入数据如何和编码分离都应该不会太陌生,本着一边学习一边分享的心态,大概总结了一下关于利用CSV.XML以及Excel来存放数据,然后在结合 ...

  9. ubuntu 下搭建redis和php的redis的拓展

    系统环境: 腾讯云服务器, ubuntu16.0.4.4 ,php7.0   一.安装redis服务 sudo apt-get install redis-server 安装好的redis目录在 /e ...

  10. Windows server 2008 r2下载地址和激活破解方法

    Windows 7发布了服务器版本——Windows Server 2008 R2.同2008年1月发布的Windows Server 2008相比,Windows Server 2008 R2继续提 ...