【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;
}
posted @ 2020-05-31 19:37  asuldb  阅读(230)  评论(0编辑  收藏  举报