题目链接
题意:给一棵N个点的树,每条边有Ai、Bi权值可以选,现在问的是选K条边作为A边,其余N-1-K条边为B边,求最短直径。
一开始的时候,想直接在树上做一个DP,但是写完之后发现不对劲,如果我们直接在树上写DP的话,由于他们的关系是相互制约的,所以确实不大好维护,因为这个dp要考虑从祖先节点的另外的方向的节点。
所以,我二分了一个答案,假定现在直径为,接下去维护点u为根的子树,当选择kk个A边的时候,此时的可行解中的最小链长是多少?只要使得1节点(也就是根节点)存在合法解就可以证明此时的是可行的。
所以,依然是维护一个dp,只是此时的dp是有限制的,表示的是第i个节点为根的子树中,当取j个A边的时候,此时的可行解中最短的链长是多少?
在确定可行的时候,dp方程就可以表示出来了:(前提可行解)
,此时加的是A边情况;
,此时加的是B边的情况。
判断可行解,就是要求他们的边的和是小于等于的。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
namespace fastIO {
#define BUF_SIZE 100000
//fread -> read
bool IOerror = 0;
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if(p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if(pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
inline bool read(int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror) return false;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
return true;
}
#undef BUF_SIZE
};
using namespace fastIO;
const int maxN = 2e4 + 7;
int N, K, head[maxN], cnt;
struct Eddge
{
int nex, to; ll a, b;
Eddge(int _n=-1, int _t=0, ll _a=0, ll _b=0):nex(_n), to(_t), a(_a), b(_b) {}
} edge[maxN << 1];
inline void addeddge(int u, int v, ll a, ll b)
{
edge[cnt] = Eddge(head[u], v, a, b);
head[u] = cnt++;
}
inline void _add(int u, int v, ll a, ll b) { addeddge(u, v, a, b); addeddge(v, u, a, b); }
int siz[maxN];
ll dp[maxN][22], cop[22], l, r, mid, ans;
void dfs(int u, int fa)
{
for(int i=0; i<=K; i++) dp[u][i] = INF;
dp[u][0] = 0;
siz[u] = 0;
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == fa) continue;
dfs(v, u);
for(int j=0; j<=min(siz[v] + siz[u] + 1, K); j++) cop[j] = INF;
for(int j=0; j<=min(siz[v], K); j++)
{
for(int kk=0; j + kk <= min(siz[u] + siz[v] + 1, K); kk++)
{
if(dp[u][kk] + dp[v][j] + edge[i].a <= mid) cop[j + kk + 1] = min(cop[j + kk + 1], max(dp[u][kk], dp[v][j] + edge[i].a));
if(dp[u][kk] + dp[v][j] + edge[i].b <= mid) cop[j + kk] = min(cop[j + kk], max(dp[u][kk], dp[v][j] + edge[i].b));
}
}
siz[u] += siz[v] + 1;
for(int j=0; j<=min(siz[u], K); j++)
{
dp[u][j] = cop[j];
}
}
}
inline void init()
{
cnt = 0; l = 1; r = 0;
for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &K);
init();
for(int i=1, u, v, a, b; i<N; i++)
{
scanf("%d%d%d%d", &u, &v, &a, &b);
_add(u, v, a, b); r += max(a, b);
}
ans = r;
while(l <= r)
{
mid = HalF;
dfs(1, 0);
if(dp[1][K] <= mid)
{
r = mid - 1;
ans = mid;
}
else l = mid + 1;
}
printf("%lld\n", ans);
}
return 0;
}