JZOJ 5939. 【NOIP2018模拟10.30】阻击计划

题解:

先把会形成偶环的非树边直接去掉。

对于形成奇环的非树边,若树上路径有交,则又会形成偶环。

于是问题转换为选若干条路径,使他们无交,且权值和最大。

注意到度数很小,n也很小,好像可以状压的样子。

f i , S f_{i,S} fi,S表示以i为根的子树中,S的第j位为0表示第j个子节点的子树中的所有边和父边都没有选,为1表示可能选了。

对于一条路径,将询问挂在lca上,在lca处讨论贡献,可能需要两个辅助数组。

Code:

#include<cstdio>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define L(x, y) e[x][++ e[x][0]] = y
#define pb push_back
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const int N = 1e3 + 5;

int n, m, x, y, z;
struct nod {
	int x, y, z;
} b[N * 5]; int b0;
int e[N][N], fa[N], bl[N], cnt[N], dep[N];

void dg(int x) {
	dep[x] = dep[fa[x]] + 1;
	fo(j, 1, e[x][0]) if(fa[x] != e[x][j]) 
		bl[e[x][j]] = ++ cnt[x], fa[e[x][j]] = x, dg(e[x][j]);
}

int lca(int x, int y) {
	while(dep[x] > dep[y]) x = fa[x];
	while(dep[x] < dep[y]) y = fa[y];
	while(x != y) x = fa[x], y = fa[y];
	return x;
}

int Xia(int x, int y) {
	if(x == y) return 0;
	while(dep[x] - 1 > dep[y]) x = fa[x];
	return x;
}

int c[N][N * 5];

int a2[15];
int f[N][N * 5], g[N], h[N][N];

int calc(int x, int y) {
	if(x == y) return 0;
	int ans = g[x];
	while(fa[x] != y) {
		int la = x; x = fa[x];
		ans += h[x][bl[la]];
	}
	return ans;
}

void dfs(int x) {
	fo(j, 1, e[x][0]) dfs(e[x][j]);
	fo(k, 0, a2[e[x][0]] - 1) fo(j, 0, e[x][0] - 1) if(k & a2[j]) f[x][k] += g[e[x][j + 1]];
	fo(j, 1, c[x][0]) {
		int i = c[x][j];
		int Z = a2[bl[Xia(b[i].x, x)] - 1] + a2[bl[Xia(b[i].y, x)] - 1];
		int v = calc(b[i].x, x) + calc(b[i].y, x) + b[i].z;
		fo(k, 0, a2[e[x][0]] - 1) if(!(k & Z))
			f[x][k | Z] = max(f[x][k | Z], f[x][k] + v);
	}
	g[x] = f[x][a2[e[x][0]] - 1];
	fo(i, 1, e[x][0]) h[x][i] = f[x][a2[e[x][0]] - 1 - a2[i - 1]];
}

int ans;

int main() {
	freopen("zujijihua.in", "r", stdin);
	freopen("zujijihua.out", "w", stdout);
	a2[0] = 1; fo(i, 1, 13) a2[i] = a2[i - 1] * 2;
	scanf("%d %d", &n, &m);
	fo(i, 1, m) {
		scanf("%d %d %d", &x, &y, &z);
		if(!z) L(x, y), L(y, x); else b[++ b0].x = x, b[b0].y = y, b[b0].z = z;
	}
	dg(1);
	fo(i, 1, n) {
		int e0 = 0;
		fo(j, 1, e[i][0]) if(e[i][j] != fa[i])
			e[i][++ e0] = e[i][j];
		e[i][0] = e0;
	}
	int B = 0;
	fo(i, 1, b0) {
		int z = lca(b[i].x, b[i].y); ans += b[i].z;
		if((dep[b[i].x] + dep[b[i].y] - 2 * dep[z]) % 2 == 0) b[++ B] = b[i], c[z][++ c[z][0]] = B;
	} b0 = B;
	dfs(1);
	printf("%d\n", ans - g[1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值