【CF671D】Roads in Yusland
设\(dp_{i,j}\)表示\(i\)子树中的边已经全部被覆盖,且子树内部选择的路径最远可以覆盖到深度为\(j\)的点的最小代价。
转移的时候大概类似一个树上背包:
\[dp_{i,j}=\min_{v\in son(i),\min k_v=j}\{\sum_{v\in son(i)}dp_{v,k_v}\}
\]
一个非常显然的事情是这样的,对于\(dp_{i,j_1},dp_{i,j_2}\),如果存在\(j_1< j_2\),且\(dp_{i,j_1}\leq dp_{i,j_2}\),即覆盖的不如你远代价却比较大,这样的\(dp_{i,j_2}\)显然是没什么用的,一定不可能成为最优答案。
于是可以只维护有用的\(dp\)状态,我们按照最远深度从小到大把\(dp\)状态存下来,显然这样\(dp\)值就是单减的。
考虑启发式合并。首先是大集合对小集合的影响,我们枚举小集合中的一个深度\(j\),令其作为最小值。需要在大集合中找到一个\(k>j\)且\(dp_k\)最小的状态来,由于\(dp\)值是单减的,所以最小值一定来自于深度最大的状态,于是我们只需要判断一下大集合中深度最大的状态是否\(>j\)了。当把状态插入到大集合的时候需要维护dp值的单调性。
再来考虑大集合对小集合的影响,显然也应该由小集合中深度最大的状态来进行转移,而且转移非常简单,就是对大集合整体加一个值。于是维护一个整体加标记即可。注意大集合中有一些状态的深度小于小集合最大深度的状态,由于转移本质下下标取\(\min\),所以转移后这些状态肯定就不存在了,直接暴力删除即可。
我们可以用set来实现上述操作,于是复杂度是\(O(n\log^2n)\)。
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=300005;
struct E{int v,nxt;}e[maxn<<1];
struct node{int d;LL w;}bl[maxn],del[maxn];
std::vector<node> r[maxn];
struct cmp {
inline bool operator() (const node &A,const node &B) {
return A.d==B.d?A.w<B.w:A.d<B.d;
}
};
inline int cop(const node &A,const node &B) {
return A.d==B.d?A.w<B.w:A.d<B.d;
}
std::set<node,cmp> s[maxn];
std::set<node>::iterator it;
int n,m,head[maxn],dep[maxn],rt[maxn],num,tot;LL va[maxn];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
e[++num].v=x;e[num].nxt=head[y];head[y]=num;
}
void dfs(int x) {
for(re int i=head[x];i;i=e[i].nxt) {
if(dep[e[i].v])continue;
dep[e[i].v]=dep[x]+1;dfs(e[i].v);
}
}
inline void kill() {puts("-1");exit(0);}
inline void ins(int a,node c) {
c.w-=va[a];
it=s[a].lower_bound((node){c.d,-1});
if(c.d==(*it).d&&c.w>=(*it).w) return;
if(it!=s[a].begin()) {
--it;if(c.w>=(*it).w)return;++it;
}
int cnt=0;
for(;it!=s[a].end();++it) {
node t=*it;
if(t.w>=c.w) del[++cnt]=t;else break;
}
while(cnt)s[a].erase(del[cnt--]);
s[a].insert(c);
}
inline void merge(int a,int b) {
it=s[a].end();--it;node t=*it;tot=0;
for(it=s[b].begin();it!=s[b].end();++it) {
bl[++tot]=*it;bl[tot].w+=va[b];
if(bl[tot].d<=t.d) bl[tot].w+=va[a]+t.w;
else {--tot;break;}
}
it=s[b].end();--it;t=*it;
while(!s[a].empty()) {
it=s[a].end();--it;node k=*it;
if(k.d>t.d) s[a].erase(k);
else break;
}
va[a]+=va[b]+t.w;
for(re int i=1;i<=tot;++i)
ins(a,bl[i]);
}
inline void out(int a) {
for(it=s[a].begin();it!=s[a].end();++it) {
printf("%d %lld\n",(*it).d,(*it).w+va[a]);
}
puts("");
}
void Dfs(int x) {
int fl=0;
for(re int i=head[x];i;i=e[i].nxt) {
if(dep[e[i].v]<dep[x]) continue;
Dfs(e[i].v);fl|=1;
if(!rt[x]) {rt[x]=rt[e[i].v];continue;}
if(s[rt[x]].size()<s[e[i].v].size()) std::swap(rt[x],rt[e[i].v]);
merge(rt[x],rt[e[i].v]);
}
if(!fl) {
rt[x]=x;
std::sort(r[x].begin(),r[x].end(),cop);
int lst=1e9+7;
for(re int i=0;i<r[x].size();++i) {
if(r[x][i].w>=lst) continue;
lst=r[x][i].w;
s[x].insert(r[x][i]);
}
if(x!=1&&s[x].empty()) kill();
return;
}
if(s[rt[x]].empty()) kill();
for(re int i=0;i<r[x].size();++i) {
it=s[rt[x]].end();--it;node t=*it;
if(r[x][i].d>=t.d) continue;
LL v=t.w+r[x][i].w+va[rt[x]];
ins(rt[x],(node){r[x][i].d,v});
}
while(x!=1&&!s[rt[x]].empty()) {
it=s[rt[x]].end();--it;node t=*it;
if(t.d>=dep[x]) s[rt[x]].erase(t);
else break;
}
if(s[rt[x]].empty()) kill();
//printf("%d:\n",x);out(rt[x]);
}
int main() {
n=read(),m=read();
for(re int i=1;i<n;i++)add(read(),read());
dep[1]=1;dfs(1);
for(re int x,y,w,i=1;i<=m;i++){
x=read(),y=read(),w=read();
if(x!=y)r[x].push_back((node){dep[y],w});
}
Dfs(1);it=s[rt[1]].begin();
printf("%lld\n",(*it).w+va[rt[1]]);
return 0;
}