bzoj1808 [Ioi2007]training 训练路径 树形dp+状压dp

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/83549160

Description


马克(Mirko)和斯拉夫克(Slavko)正在为克罗地亚举办的每年一次的双人骑车马拉松赛而紧张训练。他们需要选择一条训练路径。 他们国家有N个城市和M条道路。每条道路连接两个城市。这些道路中恰好有N-1条是铺设好的道路,其余道路是未经铺设的土路。幸运的是,每两个城市之间都存在一条由铺设好的道路组成的通路。换句话说,这N个城市和N-1条铺设好的道路构成一个树状结构。 此外,每个城市最多是10条道路的端点。 一条训练路径由某个城市开始,途经一些道路后在原来起始的城市结束。因为马克和斯拉夫克喜欢去看新城市,所以他们制定了一条规则:绝不中途穿越已经去过的城市,并且绝不在相同的道路上骑行两次(不管方向是否相同)。训练路径可以从任何一个城市开始,并且不需要访问所有城市。 显然,坐在后座的骑行者更为轻松,因为坐在前面的可以为他挡风。为此,马克和斯拉夫克在每个城市都要调换位置。为了保证他们的训练强度相同,他们要选择一条具有偶数条道路的路径。 马克和斯拉夫克的竞争者决定在某些未经铺设的土路上设置路障,使得他们两人不可能找到满足上述要求的训练路径。已知在每条土路上设置路障都有一个费用值(一个正整数),并且竞争者不能在铺设好的道路上设置路障。 任务 给定城市和道路网的描述,写一个程序计算出为了使得满足上述要求的训练路径不存在,而需要的设置路障的最小总费用。

输入的第一行包含两个整数N和M,(2≤N≤1000,N-1≤M≤5000),分别表示城市和道路的个数。 接下来的M行每行包含3个整数A, B和C(1≤A≤N, 1≤B≤N, 0≤C≤10 000), 用来描述一条道路。A和B是不同的整数,表示由这条道路直接相连的两个城市。对于铺设好的道路C是0;对于土路,c是在该条路上设置路障所需的费用值。 每个城市最多是10条道路的端点。任意两个城市都不会有多于一条直接相连的道路。

Solution


题目好长!好劲!

首先如果一条非树边连通树边形成了偶环显然不阔以。接着就可以发现若存在奇环套奇环也不阔以。于是我们需要花费最少代价把原变成一棵仙人掌,然后我就不会做了

首先把边权都加起来,问题变成求最大代价添加一些边使得原变成仙人掌
我们把连一条非树边视为在两端点的树的路径上打标记,那么原图为仙人掌当且仅当每条边不会被标记超过一次
观察我们连一条(x,y,w)的非树边会造成什么影响,很显然就是x到y的链上节点连向父亲的边都被覆盖了
在这里插入图片描述

如图,我们在红色点之间连非树边,那么蓝色的边都被覆盖了,也即是它们不能再属于另一个环
这有什么用嘞,注意到每个节点度数不超过10,考虑状压每个节点儿子的状态,f[i,S]表示节点i的儿子中除去状态为S的儿子节点的最大答案。我们枚举两端点lca为此时i的边,那么f[i]可以通过x和y暴力向上跳转移。具体的柿子不好写出来可以看着图推一推

其实还可以考虑n开大要怎么做。注意到lca和链上求和都可以树链剖分,于是就没了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=5005;

struct edge {int x,y,next;} e[N*2];

std:: vector <edge> vec[N];

int dep[N],fa[N];
int f[N][2050],fr[N];
int ls[N],edCnt;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

void dfs1(int now,int from) {
	int cnt=-1;
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y==fa[now]) continue;
		fr[e[i].y]=++cnt; fa[e[i].y]=now; dep[e[i].y]=dep[now]+1;
		dfs1(e[i].y,cnt);
	}
}

void dfs2(int now) {
	int size=0;
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y==fa[now]) continue;
		dfs2(e[i].y); size++;
	}
	int lim=(1<<size)-1;
	rep(i,0,lim) {
		int sum=0;
		for (int j=ls[now];j;j=e[j].next) {
			if (e[j].y==fa[now]||((i>>fr[e[j].y])&1)) continue;
			sum+=f[e[j].y][0];
		}
		f[now][i]=sum;
	}
	for (int i=0;i<vec[now].size();++i) {
		edge wjp=vec[now][i];
		int tmp=0,sum=wjp.next;
		if (wjp.x!=now) {
			sum+=f[wjp.x][0];
			while (fa[wjp.x]!=now) {
				sum+=f[fa[wjp.x]][1<<fr[wjp.x]];
				wjp.x=fa[wjp.x];
			}
			tmp|=(1<<fr[wjp.x]);
		}
		if (wjp.y!=now) {
			sum+=f[wjp.y][0];
			while (fa[wjp.y]!=now) {
				sum+=f[fa[wjp.y]][1<<fr[wjp.y]];
				wjp.y=fa[wjp.y];
			}
			tmp|=(1<<fr[wjp.y]);
		}
		for (int st=lim^tmp,lxf=st;;lxf=(lxf-1)&st) {
			f[now][lxf]=std:: max(f[now][lxf],f[now][lxf|tmp]+sum);
			if (!lxf) break;
		}
	}
}

int get_lca(int x,int y) {
	if (dep[x]<dep[y]) std:: swap(x,y);
	while (dep[x]>dep[y]) x=fa[x];
	while (x!=y) x=fa[x],y=fa[y];
	return x;
}

int main(void) {
	freopen("zujijihua.in","r",stdin);
	freopen("zujijihua.out","w",stdout);
	int n=read(),m=read(),ans=0;
	rep(i,1,m) {
		int x=read(),y=read(),w=read();
		if (!w) add_edge(x,y);
		else vec[0].push_back((edge) {x,y,w});
		ans+=w;
	}
	dfs1(dep[1]=1,-1);
	for (int i=0;i<vec[0].size();++i) {
		edge wjp=vec[0][i];
		int lca=get_lca(wjp.x,wjp.y);
		if ((dep[wjp.x]+dep[wjp.y]-dep[lca]*2)%2==0) {
			vec[lca].push_back(wjp);
		}
	}
	dfs2(1);
	printf("%d\n", ans-f[1][0]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/83549160