1. 程式人生 > >[JZOJ5939]【NOIP2018模擬10.30】阻擊計劃

[JZOJ5939]【NOIP2018模擬10.30】阻擊計劃

Description

小R和小Z打算在這個週末一起騎車在G國的城市看風景,G國的城市有n個城市,m條雙向道路,這m條邊中,有n-1條道路已經鋪設完畢,任意兩個城市之間都有一條由鋪設好的道路組成的路徑。
由於G國經常收到周圍強大力場的影響,**G國的每個城市至多是十條道路的端點(**包括鋪設好和未鋪設好的道路)。
小R和小Z制訂了這樣一個Van耍計劃:從一個城市開始,沿著G國的道路騎行,途中不經過之前已經去過的城市,也不經過之前去過的道路,最後回到起點城市。
由於他們騎得是雙人自行車,前排的座位比後排的作為更累,他們決定每次到達一個城市都會換一次位置,為了保證每個人的體力消耗相同,繼續進行他們下面的遊戲,他們需要一條恰好有偶數條道路的路徑。
為了阻止他們,小J決定破壞一些沒有被鋪設好的道路,由於自身能力不足,他找到了你,並將自己一週的研究資料——破壞每條未被鋪設好的道路的花費告訴了你,希望你幫他算出他至少需要花費多少代價才能阻止小R和小Z的計劃。
對於所有資料 n≤1000,m≤5000,每條道路的花費≤10000

Solution

題目大意:給定一棵n個節點m條邊的無向圖,每條邊有一個刪除所需要的花費,這m條邊中有n-1條是不能刪除的,並且它們構成一個生成樹。
求最少的花費刪掉一些非樹邊使得原圖不存在邊數為偶數的環。

我們在這棵生成樹上考慮

假設對於一條非樹邊,它本身+樹邊構成了一個偶環,那顯然它是要直接刪掉的,直接累加。

如果它+樹邊構成了一個奇環,我們考慮它們什麼時候會變成偶環。
我們不妨把這條非樹邊對應成兩個端點在樹上的路徑,可以發現,如果任意兩條這樣的路徑出現了重疊邊(重疊點是可以的),那麼就會出現偶環。

問題轉化為,我們最多能保留多少代價和的鏈,使得這些鏈沒有重疊邊。

考慮將這些鏈都掛在它們的LCA上進行DP

F [ i ] [ S ] F[i][S]

表示假設已對於i為根的子樹,S是一個二進位制狀態,表示S所有的兒子邊的覆蓋情況。第j位為1代表i的第j個兒子被覆蓋了(當然為了方便轉移,可以將定義改成“至多”,相當於取子集max,即為1代表可能被覆蓋也可能沒有,為0則一定沒有)

對於一個點,列舉每條以它為LCA的鏈,轉移相當於對於這條鏈上的所有點k的 F [ k ] [ S ] F[k][S'] ,S’的除了鏈上的兒子是0,其他都是1

對於i,我們剛剛更新的和可以與所有 F [ i ] [ T ] F[i][T] ,且T的這條鏈的兩個兒子都為0合併成 F [ i ] [ T ] F[i][T']

此外,還可以直接對於每個兒子直接繼承,並將這個兒子邊設為已覆蓋
這樣對於每條鏈都 2 10 2^{10} 合併一次,每個兒子也都 2 10 2^{10} 合併一次,複雜度就是 ( n + m ) 2 10 (n+m)*2^{10}

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 5005
using namespace std;
int n,m,f[1005][2048],fa[N][20],dep[N],a[N][5],fs[N],nt[2*N],dt[2*N],num,m1,ans,s1[N],n1,fn[N],cf[12];
vector<int> dq[N];
int lca(int x,int y)
{
	if(dep[x]>dep[y]) swap(x,y);
	for(int j=dep[y]-dep[x],c=0;j;c++,j>>=1) if(j&1) y=fa[y][c];
	for(int j=19;x!=y;)
	{
		while(j&&fa[x][j]==fa[y][j]) j--;
		x=fa[x][j],y=fa[y][j];
	}
	return x;
}
int jump(int x,int f)
{
	if(x==f) return x;
	for(int j=dep[x]-dep[f]-1,c=0;j;c++,j>>=1) if(j&1) x=fa[x][c];
	return x;
}
void dfs(int k)
{	
	dep[k]=dep[fa[k][0]]+1;
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa[k][0])
		{
			fa[p][0]=k;
			dfs(p);
		}
	}
}
void link(int x,int y)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
}
void dp(int k)
{
	int c=0;
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa[k][0]) 
		{
			dp(p),fn[p]=c++;
			fo(j,0,2047) if(!(j&cf[fn[p]]))f[k][j^cf[fn[p]]]=max(f[k][j^cf[fn[p]]],f[k][j]+f[p][2047]);
		}
	}
	int l=dq[k].size();
	fo(i,0,l-1)
	{
		int t=dq[k][i],x=a[t][0],y=a[t][1],s=0,lx=-1,ly=-1;
		while(x!=k) 
		{
			if(lx<0) s+=f[x][cf[11]-1];
			else s+=f[x][(cf[11]-1)^cf[lx]];
			lx=fn[x],x=fa[x][0];
		}
		while(y!=k)
		{
			if(ly<0) s+=f[y][cf[11]-1];
			else s+=f[y][(cf[11]-1)^cf[ly]];
			ly=fn[y],y=fa[y][0];
		}
		x=a[t][3],y=a[t][4],lx=0;
		if(x!=k) lx^=cf[fn[x]];
		if(y!=k) lx^=cf[fn[y]];
		fo(j,0,2047)
		{
			if(!(j&lx)) f[k][j^lx]=max(f[k][j^lx],s+a[t][2]+f[k][j]);		
		}
	}
}
int main()
{
	cin>>n>>m;
	fo(i,1,m)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		if(z==0) link(x,y),link(y,x);
		else a[++num][0]=x,a[num][1]=y,a[num][2]=z;
	}
	dfs(1);
	fo(i,0,11) cf[i]=1<<i;
	fo(j,1,19)
		fo(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
	ans=0;
	fo(i,1,num)
	{
		int x=a[i][0],y=a[i][1],p=lca(x,y);
		if((dep[x]+dep[y]-2*dep[p]+1)%2==0) ans+=a[i][2];
		else dq[p].push_back(i),a[i][3]=jump(x,p),a[i][4]=jump(y,p),ans+=a[i][2];
	}
	dp(1);
	int sv=0;
	fo(j,0,2047) sv=max(sv,f[1][j]);
	printf("%d\n",ans-sv);
}