[Luogu P4649] [BZOJ 1808] [IOI2007] training 训练路径

洛谷传送门

BZOJ传送门

题目描述

马克(Mirko)和斯拉夫克(Slavko)正在为克罗地亚举办的每年一次的双人骑车马拉松赛而紧张训练。他们需要选择一条训练路径。

他们国家有 N N N个城市和 M M M条道路。每条道路连接两个城市。这些道路中恰好有 N − 1 N-1 N1条是铺设好的道路,其余道路是未经铺设的土路。幸运的是,每两个城市之间都存在一条由铺设好的道路组成的通路。换句话说,这 N N N个城市和 N − 1 N-1 N1条铺设好的道路构成一个树状结构。

此外,每个城市最多是 10 10 10条道路的端点。

一条训练路径由某个城市开始,途经一些道路后在原来起始的城市结束。因为马克和斯拉夫克喜欢去看新城市,所以他们制定了一条规则:绝不中途穿越已经去过的城市,并且绝不在相同的道路上骑行两次(不管方向是否相同)。训练路径可以从任何一个城市开始,并且不需要访问所有城市。

显然,坐在后座的骑行者更为轻松,因为坐在前面的可以为他挡风。为此,马克和斯拉夫克在每个城市都要调换位置。为了保证他们的训练强度相同,他们要选择一条具有偶数条道路的路径。

马克和斯拉夫克的竞争者决定在某些未经铺设的土路上设置路障,使得他们两人不可能找到满足上述要求的训练路径。已知在每条土路上设置路障都有一个费用值(一个正整数),并且竞争者不能在铺设好的道路上设置路障。

给定城市和道路网的描述,写一个程序计算出为了使得满足上述要求的训练路径不存在,而需要的设置路障的最小总费用。

输入输出格式

输入格式:

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

输出格式:

输出包含一个整数,表示求出的最小总费用。

输入输出样例

输入样例#1:

5 8 
2 1 0 
3 2 0 
4 3 0 
5 4 0 
1 3 2 
3 5 2 
2 4 5 
2 5 1 

输出样例#1:

5

输入样例#2:

9 14 
1 2 0 
1 3 0 
2 3 14 
2 6 15 
3 4 0 
3 5 0 
3 6 12 
3 7 13 
4 6 10 
5 6 0 
5 7 0 
5 8 0 
6 9 11 
8 9 0 

输出样例#2:

48

说明

[Luogu P4649] [BZOJ 1808] [IOI2007] training 训练路径_第1张图片

第一个样例中道路与城市的布置。已被铺设好的道路以粗边显示。

[Luogu P4649] [BZOJ 1808] [IOI2007] training 训练路径_第2张图片

共有 5 5 5种可能的路线。如果边 1 − 3 1-3 13 3 − 5 3-5 35 2 − 5 2-5 25被封锁,则两人将会不能使用 5 5 5种路线的任何一种。封锁这三条边的代价是 5 5 5

只封锁两条边,像是 2 − 4 2-4 24 2 − 5 2-5 25,也是可以的,但这样会导致较高的代价, 6 6 6

在前 30 30 30分的测试数据中,铺设好的道路会形成一条链。

解题分析

首先可以先把和树上边本来就成偶环的边去掉。

把题目转化为选出一些边使得其权值和最大。 由于现在一条边连接的两个点在树上的距离为偶数, 很显然如果选出的两条边有交则会形成一个新的偶环。 又注意到每个点的点度很小, 直接状压一个点和哪些点之间的边被用过就好了。

总复杂度 O ( M × ( 2 10 + l o g ( N ) + M ) O(M\times (2^{10}+log(N)+M) O(M×(210+log(N)+M)(因为要求个LCA,还要暴力枚举下面的状态)。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 1050
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, m, ans;
struct Edge {int x, y, val;};
std::vector <int> to1[MX], to2[MX];
std::vector <Edge> que[MX], edge;
int dep[MX], fat[MX][12], dp[MX][1100], pos[MX];
void pre(R int now)
{
	for (R int i = 1; i <= 11; ++i)
	fat[now][i] = fat[fat[now][i - 1]][i - 1];
	for (auto i : to1[now])
	{
		if (i == fat[now][0]) continue;
		dep[i] = dep[now] + 1;
		fat[i][0] = now;
		to2[now].push_back(i);
		pos[i] = to2[now].size() - 1;
		pre(i);
	}
}
IN int LCA(R int x, R int y)
{
	if (dep[x] < dep[y]) std::swap(x, y);
	int del = dep[x] - dep[y];
	for (R int i = 0; i <= 11; ++i)
	if ((del >> i) & 1) x = fat[x][i];
	if (x == y) return x;
	for (R int i = 11; ~i; --i)
	if (fat[x][i] ^ fat[y][i])
	x = fat[x][i], y = fat[y][i];
	return fat[x][0];
}
void DP(R int now)
{
	int sum, ns, p, las1, las2;
	for (auto i : to2[now]) DP(i);
	for (R int s = (1 << to2[now].size()) - 1; ~s; --s)
	for (R int i = to2[now].size() - 1; ~i; --i)
	if (!(s & (1 << i))) dp[now][s] += dp[to2[now][i]][0];
	for (auto &cur : que[now])
	{
		if (cur.x != now && cur.y != now)
		{
			sum = dp[cur.x][0] + dp[cur.y][0]; las1 = cur.x, las2 = cur.y;
			for (p = fat[las1][0]; p ^ now; las1 = p, p = fat[p][0])
			sum += dp[p][1 << pos[las1]];
			for (p = fat[las2][0]; p ^ now; las2 = p, p = fat[p][0])
			sum += dp[p][1 << pos[las2]];
			ns = (1 << pos[las1]) | (1 << pos[las2]);
		}
		else
		{
			if (cur.x == now) std::swap(cur.x, cur.y);
			sum = dp[cur.x][0]; las1 = cur.x;
			for (p = fat[las1][0]; p ^ now; las1 = p, p = fat[p][0])
			sum += dp[p][1 << pos[las1]];
			ns = 1 << pos[las1];
		}
		sum += cur.val;
		for (R int s = (1 << to2[now].size()) - 1; ~s; --s)
		if (!(s & ns)) dp[now][s] = max(dp[now][s], dp[now][s | ns] + sum);
	}
}
int main(void)
{
	int foo, bar, l; in(n), in(m);
	for (R int i = 1; i <= m; ++i)
	{
		in(foo), in(bar), in(l);
		if (!l) to1[foo].push_back(bar), to1[bar].push_back(foo);
		else edge.push_back({foo, bar, l}), ans += l;
	}
	pre(1);
	for (auto i : edge)
	{
		foo = LCA(i.x, i.y);
		if ((dep[i.x] + dep[i.y] - dep[foo] * 2) & 1) continue;
		que[foo].push_back(i);
	}
	DP(1);
	printf("%d\n", ans - dp[1][0]);
}

你可能感兴趣的:(动态规划,状态压缩,动态规划,状态压缩)