【2018-国庆雅礼-NOIP-培训】-D3T3- w

题解:

我们考虑最后翻转的边集 $S$ ,易知最小操作数为 ${V,S}$ 中奇数度数的点的一半,最小操作总长为 $|S|$。

那么我们可以使用树形 dp ,用 $dp[i][0/1]$ 记录以 $i$ 为根的子树内,$i$ 与父亲之间的边是否翻转,最少的奇度 数点数、此时最小总长度。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
#define maxn 100010

using namespace std;

const pair<int,int> inf=make_pair(1e9,1e9);
int n;
vector<pair<int,int> > g[maxn];
pair<int,int> dp[maxn][2];

inline pair<int,int> operator+ (pair<int,int> a,pair<int,int> b)
{
return make_pair(a.first+b.first,a.second+b.second);
}
void dfs(int pos,int fa,int type)
{
pair<int,int> tmp0(0,0),tmp1(inf);
for(int i=0,v;i<g[pos].size();++i)
if((v=g[pos][i].first)!=fa)
{
dfs(v,pos,g[pos][i].second);
pair<int,int> nxt0,nxt1;
nxt0=min(tmp0+dp[v][0],tmp1+dp[v][1]);
nxt1=min(tmp1+dp[v][0],tmp0+dp[v][1]);
tmp0=nxt0;tmp1=nxt1;
}
if(type==0||type==2)
dp[pos][0]=min(tmp0,make_pair(tmp1.first+1,tmp1.second));
else
dp[pos][0]=inf;
if(type==1||type==2)
dp[pos][1]=min(make_pair(tmp0.first+1,tmp0.second+1),make_pair(tmp1.first,tmp1.second+1));
else
dp[pos][1]=inf;
}

int main()
{
freopen("w.in","r",stdin);
freopen("w.out","w",stdout);
scanf("%d",&n);
for(int i=1,a,b,c,d;i<n;++i)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
if(d!=2)
d=(c!=d);
g[a].push_back(make_pair(b,d));
g[b].push_back(make_pair(a,d));
}
dfs(1,0,0);
printf("%d %d\n",dp[1][0].first/2,dp[1][0].second);
return 0;
}