P4649 - 训练路径 tijie

好神仙的 IOI 题。。。这个不仅往儿子转移,而往更深层的后代转移的树形 DP 就很 nb。

首先注意到,破坏树上环显然只能破坏非树边。那么对所有偶树上环都直接要把非树边破坏掉,剩下来一些奇树上环。如果奇树上环有交,那么显然能叠成偶环;否则显然无偶环。所以我们就是要删掉一些奇树上环使剩下的所有环不交,并且最大化剩下的权值和。

最大权独立集?先不说二分图最大权独立集能不能做,首先这就不是二分图,随便三个互相重叠的环就废了。所以我们需要利用树结构的特殊性 DP 来搞。

考虑在子树 \(i\)​ 内决策 LCA 等于 \(i\)​​ 的环然后转移下去。如果加入一条链的话,我们将这条链上的边全删掉,剩下来若干个连通块,除了一个其它每个连通块都是某子树删或不删某个儿子树的状态,特例是 \(i\)​ 所在连通块,是子树 \(i\)​ 删两个儿子树。它们显然是独立的(因不连通),所以可以分别求然后加起来。那么我们要记录再 \(j,k\)​ 分别表示 \(i\)​ 删除的儿子树吗?如果那样,那对于每个状态还可能再删,删到四个,永无止境地迭代下去。但注意到 \(n,m,2^{\deg}\)​ 这三者都是 1e3 级别,两两相乘加起来不会炸。\(\deg\)​ 就是儿子树的个数,那么我们可以对删掉的儿子树集合进行状压,这样就可以了。

转移的话分两种,一种是不取任何 LCA 等于 \(i\)​ 的环,那么就往 mask 里的儿子转移。另一种就取一个转移到自己的不同 mask 以及后代们。这样复杂度对每个环处理的时间是 \(\mathrm O(n)\)​,总复杂度是 \(\mathrm O\!\left(nm2^{\deg}\right)\)​,爆炸。不过我们可以在枚举 \(2^{\deg}\)​​ 外面把每个环的代价预处理好,这样就把 \(2^{\deg}\)\(m\) 平行化掉了。

code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=5010,LOG_N=15;
int n,m;
vector<int> nei[N];
int cx[N],cy[N],cv[N],ca[N];
int fa[N][LOG_N],dep[N];
int id[N/5][N/5];
void dfs(int x=1){
	for(int i=1;i<LOG_N;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	int now=0;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa[x][0])continue;
		id[x][y]=now++;
		fa[y][0]=x;
		dep[y]=dep[x]+1;
		dfs(y);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=LOG_N-1;~i;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
	if(x==y)return x;
	for(int i=LOG_N-1;~i;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int dp[N/5][1<<10];
vector<int> vv[N];
int add[N],msk[N];
void dfs0(int x=1){
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa[x][0])continue;
		dfs0(y);
	}
	for(int i=0;i<vv[x].size();i++){
		int y=vv[x][i],u=cx[y],v=cy[y];
		add[y]=cv[y];
		if(u!=x){
			add[y]+=dp[u][0];
			while(fa[u][0]!=x)add[y]+=dp[fa[u][0]][1<<id[fa[u][0]][u]],u=fa[u][0];
			msk[y]|=1<<id[x][u];
		}
		if(v!=x){
			u=v;
			add[y]+=dp[u][0];
			while(fa[u][0]!=x)add[y]+=dp[fa[u][0]][1<<id[fa[u][0]][u]],u=fa[u][0];
			msk[y]|=1<<id[x][u];
		}
	}
	for(int i=(1<<nei[x].size()-(x!=1))-1;~i;i--){
		for(int j=0;j<nei[x].size();j++){
			int y=nei[x][j];
			if(y==fa[x][0])continue;
			if(!(i>>id[x][y]&1))dp[x][i]+=dp[y][0];
		}
		for(int j=0;j<vv[x].size();j++){
			int y=vv[x][j];
			if(!(msk[y]&i))dp[x][i]=max(dp[x][i],add[y]+dp[x][i|msk[y]]);
		}
	}
}
int main(){dep[1]=1;
	cin>>n>>m;
	int now=0;
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		if(!z)nei[x].pb(y),nei[y].pb(x);
		else cx[++now]=x,cy[now]=y,cv[now]=z;
	}
	m=now;
	dfs();
	int ans=0;
	for(int i=1;i<=m;i++)ca[i]=lca(cx[i],cy[i]),ans+=cv[i],dep[cx[i]]%2==dep[cy[i]]%2&&(vv[ca[i]].pb(i),0);
	dfs0();
	cout<<ans-dp[1][0];
	return 0;
}
posted @ 2021-09-18 14:35  ycx060617  阅读(113)  评论(0编辑  收藏  举报