这仍然是最小链覆盖问题,转化为求最大反链。
然后考虑用上树的性质进行优化:条件集合 (S) 构成反链 (Leftrightarrow) 选定一个没有条件的节点 (rt) 做根,对 (rt) 的所有儿子 (x) 的子树 ( ext{sub}(x)) 与 (S) 的交构成反链,且存在时刻 (T) 满足 (forall(v,d)in S),(d+ ext{dep}_v>T) 且 (d- ext{dep}_v<T)。
注意 (T) 不一定是整数,但是若将所有边权以及 (d) 变为两倍,(T) 就是整数了。
于是就可以 dp 了,设 (f_{u,t}) 表示考虑 (u) 的子树(除了 (u)),(T=t) 的时候的最大反链。
考虑将 (f_u) 合并进父亲:设贡献为 (f'_u),(u) 与父亲的连边长度为 (l),若 (u) 没有元素,那么就是 (f_{u,i}'=max_{j=i-l}^{i+l}f_{u,j}),称这个操作为向上走 (l) 步。
那如果有元素怎么办,设条件 ((u,d)) 上有 (x) 条,考虑到贡献时刻为 ([d-l+1,d+l-1]),应当是向上走 (1) 步,然后 (f_{u,d}':=max(f_{u,d}',f_{u,d}+x)),然后向上走 (l-1) 步。
dp 数组很大存不下,但都是一大段相同的,所以考虑维护差分数组。
向上走 (l) 步这个操作就是正数向左 (l) 步,负数向右 (l) 步,相遇的正负抵消。
所以可以用 map 存差分数组,用懒标记 (add) 表示向上走了多少步,用 priority_queue 维护相邻的负数和正数,按距离排序,走 (l) 步的时候就把距离 (leq 2(add+l)) 的合并,然后 (add:=add+l)。
合并的时候就把差分数组加起来,所以启发式合并就可以了。
时间复杂度 (O(n+klog^2k))。
#include<bits/stdc++.h>
#define fi first
#define se second
#define PB emplace_back
using namespace std;
typedef pair<int, int> pii;
const int N = 100003;
int rd(){
int ch = getchar(), x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
return x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n;
vector<pii> G[N], V[N];
struct DS {
map<int, int> ps, ng; int tag;
priority_queue<pii, vector<pii>, greater<pii> > pq;
void reb(int x, bool op){
if(op){ // positive
auto it = ng.upper_bound(x-(tag<<1));
if(it == ng.begin()) return; --it;
pq.emplace(x-it->fi, x);
// printf("pq <- %d, %d
", it->fi, x);
} else { // negative
auto it = ps.lower_bound(x+(tag<<1));
if(it == ps.end()) return;
pq.emplace(it->fi-x, it->fi);
// printf("pq <- %d, %d
", x, it->fi);
}
}
void ins(int x, int val){
if(val > 0){
ps[x+tag] += val;
reb(x+tag, true);
} else {
ng[x-tag] += val;
reb(x-tag, false);
}
}
int get() const {
int res = 0, now = 0;
auto i = ps.begin(), j = ng.begin();
while(i != ps.end() && j != ng.end()){
if(i->fi-j->fi>=(tag<<1)){
now += j->se; ++j;
} else {
now += i->se; ++i;
} chmax(res, now);
} return res;
}
void work(int l){
while(!pq.empty()){
pii _ = pq.top();
if(_.fi > (tag+l<<1)) break;
pq.pop();
int x = _.se, y = x-_.fi;
auto i = ps.find(x), j = ng.find(y);
if(i == ps.end() || j == ng.end()) continue;
if(i->se + j->se < 0){
j->se += i->se; ps.erase(i); reb(j->fi, false);
} else {
// printf("x = %d, y = %d
", x, y);
i->se += j->se; ng.erase(j); reb(i->fi, true);
// printf("ng.erase(j)
");
}
} tag += l;
}
size_t size() const {return ps.size()+ng.size();}
int qry(int p) const {
auto i = ps.find(p+tag), j = ng.find(p-tag);
return (i==ps.end()?0:i->se)+(j==ng.end()?0:j->se);
}
void operator += (const DS &o){
for(pii i : o.ps) ins(i.fi-o.tag, i.se);
for(pii i : o.ng) ins(i.fi+o.tag, i.se);
}
} S[N];
void dfs(int x, int fa){
for(pii _ : G[x]) if(_.fi != fa){
int v = _.fi, len = _.se;
dfs(v, x);
for(pii &i : V[v]) i.se -= max(0, max(-S[v].qry(i.fi), S[v].qry(i.fi+1)));
S[v].work(1);
for(pii i : V[v]) if(i.se > 0){
S[v].ins(i.fi, i.se);
S[v].ins(i.fi+1, -i.se);
}
S[v].work(len-1);
if(S[x].size() < S[v].size()) swap(S[x], S[v]);
S[x] += S[v];
}
}
int main(){
n = rd();
for(int i = 1;i < n;++ i){
int u = rd(), v = rd(), l = rd()<<1;
G[u].PB(v, l); G[v].PB(u, l);
}
int m = rd(); G[0].PB(1, 2);
while(m --){
int d = rd()<<1, f = rd(), p = rd();
V[p].PB(d, f);
} dfs(0, 0);
printf("%d
", S[0].get());
}