题解:P14309 【MX-S8-T2】配对
题解:P14309 【MX-S8-T2】配对
思路
注:本文中黑色节点即为 ci=1c_i=1ci=1 的节点,白色节点为 ci=0c_i=0ci=0 的节点。
先考虑没有修改的情况。
如果在以 xxx 为根的一颗子树内有偶数个黑色节点,则 xxx 与他的父节点之间的边对答案没有贡献,因为该子树内的黑色节点之间可以两两匹配,这种情况一定比子树内的黑色节点连到外面更优。如果黑色节点个数为奇数,则一定有且仅有一个黑色节点连到子树外的黑色节点,这时需要经过 xxx 与其父节点的连边一次,所以这条边对答案有贡献。
再考虑修改的情况。
修改中交换两点的颜色实际上是将两点的颜色反转,可以视为将一个黑色节点变白,将一个白色节点变黑。不过还要注意如果一开始黑色节点数量为奇数,我们需要无视一个黑色节点,即多将一个黑色节点变白。
dpx,i,jdp_{x,i,j}dpx,i,j,表示以 iii 为根的子树中将 iii 个节点变白,将 jjj 个节点变黑时 rrr 的最小值。
转移方程就比较简单了,输出时当黑色节点总数为奇数时输出 min(dprt,1,0,dprt,2,1)\min(dp_{rt,1,0},dp_{rt,2,1})min(dprt,1,0,dprt,2,1),为偶数时输出 min(dprt,0,0,dprt,1,1)\min(dp_{rt,0,0},dp_{rt,1,1})min(dprt,0,0,dprt,1,1)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,c[1000010],sz[1000010];
ll dp[1000010][3][2],u[1000010][3][2];
struct N{int y;ll v;
};
vector<N> e[1000010];
inline ll min(ll a,ll b){return a<b?a:b;}
void dfs(int x,int xfa){sz[x]=c[x];dp[x][0][0]=dp[x][c[x]&1][c[x]^1]=0;for(N i:e[x])if(i.y!=xfa){int y=i.y,v=i.v;dfs(y,x);sz[x]+=sz[y];for(int i=0;i<=2;i++)for(int j=0;j<=1;j++)u[x][i][j]=1e18;for(int i1=0;i1<=2;i1++)for(int i2=0;i2<=2;i2++)for(int j1=0;j1<=1;j1++)for(int j2=0;j2<=1;j2++)if(i1+i2<=2&&j1+j2<=1)u[x][i1+i2][j1+j2]=min(u[x][i1+i2][j1+j2],dp[x][i1][j1]+dp[y][i2][j2]+v*((sz[y]-i2+j2+2)&1));for(int i=0;i<=2;i++)for(int j=0;j<=1;j++)dp[x][i][j]=u[x][i][j];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;for(int i=1;i<=n;i++)cin>>c[i];for(int i=1,x,y,v;i<n;i++){cin>>x>>y>>v;e[x].push_back({y,v});e[y].push_back({x,v});}for(int x=1;x<=n;x++)for(int i=0;i<=2;i++)for(int j=0;j<=1;j++)dp[x][i][j]=1e18;dfs(1,0);cout<<((sz[1]&1)?min(dp[1][1][0],dp[1][2][1]):min(dp[1][0][0],dp[1][1][1]));return 0;
}