1. 程式人生 > >bzoj1808 [Ioi2007]training 訓練路徑 樹形dp+狀壓dp

bzoj1808 [Ioi2007]training 訓練路徑 樹形dp+狀壓dp

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;
}