Codeforces671D Roads in Yusland

Problem

Codeforces

Solution

被神仙同学安利的一道题目,然后想了好久都没想出来= =翻翻题解发现是道神仙题


可以把覆盖所有边的限制用矩阵的运算来描述,那么不难发现这其实是一道线性规划问题。然后这个问题不太好解决,我们可以考虑它的对偶问题。

\[\max\{c^Tx|Ax\leq b\}=\min\{b^Ty|A^Ty\geq c\} \]

正确性不会证明,只会感性地理解一下。右式的含义大概就是如果制作一个产品的成本大于等于它的收益,那么我们要最大化收益就不如把原材料直接卖了。那么这个式子的含义其实不考虑成本的最大收益等于无法获利时的最小成本,即一个临界状态,既可以做产品也可以直接卖。

由此,问题就转化成了:要求给每条边定一个权值,使得所有链覆盖的边权和小于等于它的权值,要最大化总边权。直接从深的开始贪心即可,在每个点上处理一下它的父边的选择。

时间复杂度 \(O(m\log m)\)

Code

#include <algorithm>
#include <cstdlib>
#include <cstdio>
#define pd(x) if(t[x].add)pushdown(x)
using namespace std;
typedef long long ll;
const int maxn=300010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{int v,nxt;}edge[maxn<<1];
struct leftist{
	int val,add,lc,rc,dis,del;
}t[maxn];
int n,m,p,tot,head[maxn],rt[maxn],dep[maxn];
ll ans;
void insert(int u,int v)
{
	edge[++p]=(data){v,head[u]};head[u]=p;
	edge[++p]=(data){u,head[v]};head[v]=p;
}
int new_node(int val,int del){t[++tot].val=val;t[tot].del=del;return tot;}
void pushdown(int x)
{
	int lc=t[x].lc,rc=t[x].rc;
	if(lc){t[lc].val+=t[x].add;t[lc].add+=t[x].add;}
	if(rc){t[rc].val+=t[x].add;t[rc].add+=t[x].add;}
	t[x].add=0;
}
int merge(int x,int y)
{
	if(!x||!y) return x|y;
	pd(x);pd(y);
	if(t[x].val>t[y].val) swap(x,y);
	t[x].rc=merge(t[x].rc,y);
	if(t[t[x].rc].dis>t[t[x].lc].dis) swap(t[x].lc,t[x].rc);
	t[x].dis=t[t[x].rc].dis+1;
	return x;
}
void input()
{
	int u,v,w;
	read(n);read(m);t[0].dis=-1;
	for(int i=1;i<n;i++){read(u);read(v);insert(u,v);}
	for(int i=1;i<=m;i++)
	{
		read(u);read(v);read(w);
		rt[u]=merge(rt[u],new_node(w,v));
	}
}
void dfs(int x,int pre)
{
	dep[x]=dep[pre]+1;
	for(int i=head[x];i;i=edge[i].nxt)
	  if(edge[i].v^pre)
	  {
	  	dfs(edge[i].v,x);
	  	rt[x]=merge(rt[x],rt[edge[i].v]);
	  }
	if(x==1) return ;
	while(rt[x]&&dep[t[rt[x]].del]>=dep[x]) rt[x]=merge(t[rt[x]].lc,t[rt[x]].rc);
	if(!rt[x]){puts("-1");exit(0);}
	int tmp=t[rt[x]].val;
	t[rt[x]].val-=tmp;t[rt[x]].add-=tmp;
	ans+=tmp;
}
int main()
{
	input();
	dfs(1,1);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-03-25 22:10  totorato  阅读(222)  评论(0编辑  收藏  举报