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] F[i][S]表示假设已对于i为根的子树,S是一个二进制状态,表示S所有的儿子边的覆盖情况。第j位为1代表i的第j个儿子被覆盖了(当然为了方便转移,可以将定义改成“至多”,相当于取子集max,即为1代表可能被覆盖也可能没有,为0则一定没有)
对于一个点,枚举每条以它为LCA的链,转移相当于对于这条链上的所有点k的 F [ k ] [ S ′ ] F[k][S'] F[k][S′],S’的除了链上的儿子是0,其他都是1
对于i,我们刚刚更新的和可以与所有 F [ i ] [ T ] F[i][T] F[i][T],且T的这条链的两个儿子都为0合并成 F [ i ] [ T ′ ] F[i][T'] F[i][T′]
此外,还可以直接对于每个儿子直接继承,并将这个儿子边设为已覆盖
这样对于每条链都
2
10
2^{10}
210合并一次,每个儿子也都
2
10
2^{10}
210合并一次,复杂度就是
(
n
+
m
)
∗
2
10
(n+m)*2^{10}
(n+m)∗210的
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);
}