传送
题面:给定一棵(n(1 leqslant n leqslant 30000))个结点的树,每条边包含长度(L)和费用(D)(1 leqslant L,D leqslant 1000)两个权值。要求选择一条总费用不超过(m(1leqslant leqslant 10 ^8))的路径,使得路径总长度尽量大。输入保证有解。
这道题的做法还是比较显然的。
首先肯定是点分治,因为求树上路径嘛。
然后想怎么拼接从重心出发的两条路径:对于费用为(d_i)的路径,我们肯定要在不超过(D - d_i)中找到长度最大的另一条路径。
这里很容易想到单调栈,即栈中所有路径随着费用递增,其长度必须也是递增的(就是如果有人比你小还比你强,那你就废了)。
但这有个问题。
因为在处理完每一个子树中的路径后,我们要把这些路径合并到单调栈中。但是单调栈不允许插入费用比栈顶小的元素,否则就不能保证是(O(1))的了。
所以这里我们要用set或者map维护一个可插入和删除的、长度随费用递增的序列。
所以最终的复杂度是(O(nlog^2n)).
然后set几乎是写崩了,各种边界问题,麻烦得很,所以代码就特别丑。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<set>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 3e4 + 5;
In ll read()
{
ll ans = 0;
char ch = getchar(), las = ' ';
while(!isdigit(ch)) las = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(las == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
freopen("random.in", "r", stdin);
freopen("bf.out", "w", stdout);
#endif
}
int n, K;
struct Node
{
int cos, val;
In bool operator < (const Node& oth)const
{
return cos < oth.cos;
}
In Node operator + (const Node& oth)const
{
return (Node){cos + oth.cos, val + oth.val};
}
};
struct Edge
{
int nxt, to;
Node t;
}e[maxn << 1];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y, int cos, int val)
{
e[++ecnt] = (Edge){head[x], y, (Node){cos, val}};
head[x] = ecnt;
}
bool out[maxn];
int dp[maxn], siz[maxn], cg = 0, Siz;
In void dfs1(int now, int _f, int& cg)
{
dp[now] = 0, siz[now] = 1;
forE(i, now, v)
{
if(v == _f || out[v]) continue;
dfs1(v, now, cg);
siz[now] += siz[v];
dp[now] = max(dp[now], siz[v]);
}
dp[now] = max(dp[now], Siz - siz[now]);
if(!cg || dp[now] < dp[cg]) cg = now;
}
Node a[maxn];
int cnt = 0;
In void dfs2(int now, int _f, Node t)
{
if(t.cos > K) return;
a[++cnt] = t;
forE(i, now, v) if(!out[v] && v != _f) dfs2(v, now, t + e[i].t);
}
#define sNode set<Node>::iterator
set<Node> s;
In void merge() //这个merge实在是太丑了
{
for(int i = 1; i <= cnt; ++i)
{
sNode it = s.lower_bound(a[i]), it2;
if(it != s.begin()) it2 = --it, ++it;
bool flg = 0;
if(!s.size()) flg = 1;
else if(it == s.end() && (*it2).val < a[i].val) flg = 1;
else if((*it).cos == a[i].cos && (*it).val < a[i].val) flg = 1;
else if((*it).cos > a[i].cos && (it == s.begin() || (*it2).val < a[i].val)) flg = 1;
if(flg)
{
while(it != s.end() && (*it).val <= a[i].val) s.erase(it++);
s.insert(a[i]);
}
}
}
int ans = 0;
In void solve(int now)
{
cg = 0;
dfs1(now, 0, cg);
s.clear(); s.insert((Node){0, 0});
forE(i, cg, x)
{
if(out[x]) continue;
cnt = 0;
dfs2(x, cg, e[i].t);
for(int j = 1; j <= cnt; ++j)
{
bool flg = 0;
sNode it = s.lower_bound((Node){K - a[j].cos, 0});
if(it == s.end()) --it, flg = 1;
else if((*it).cos == K - a[j].cos) flg = 1;
else if(it != s.begin()) --it, flg = 1;
if(flg) ans = max(ans, a[j].val + (*it).val);
}
merge();
}
out[cg] = 1;
forE(i, now, v) if(!out[v]) Siz = siz[v], solve(v);
}
In void init()
{
Mem(head, -1), ecnt = -1;
Mem(out, 0); ans = 0;
}
int main()
{
// MYFILE();
int T = read();
for(int id = 1; id <= T; ++id)
{
init();
n = read(), K = read();
for(int i = 1; i < n; ++i)
{
int x = read(), y = read(), cos = read(), val = read();
addEdge(x, y, cos, val), addEdge(y, x, cos, val);
}
Siz = n, solve(1);
printf("Case %d: %d
", id, ans);
}
return 0;
}