题解 w
一直觉得有点后效性什么的,也不知道怎么写
这题什么时候再康一遍,第一次见这个样子的树形DP,是个树上带不定权边的DP(???
这里能树形DP的原因好像是在这里所有子节点的状态都能表示出来
还有这里最小翻转数量可以转化为每个点翻转边的度数就挺神奇的,可以直接处理掉那几个麻烦的分类讨论
这题成链的部分分和\(d \neq 2\)的部分分其实很好做……后一个dp都不用取max,只有一种可能
全分建议再去康一眼题解
可以发现对于一个点u,如果它有一条d=2的边连到一个点v
不管这条边是否翻转,v的子树中度数为奇的点数量还有路径长度都是要原样传上来的
所以其实这条边选不选只影响u,v度数的奇偶性
而对v的影响其实即为其父节点与它是否连边,这个在设计状态的时候已经考虑进去了
所以只需要开两个二元组分别记录这个点连出的边数是奇数还是偶数时的最小值就行了
随时默念「奇加奇等于偶,偶加奇等于奇」能避免很多智障错误……
细节极多,写了5个小时……
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int head[N], size, cnt[N];
struct edge{int to, next, val;}e[N<<1];
inline void add(int s, int t, int w) {edge* k=&e[++size]; k->to=t; k->val=w; k->next=head[s]; head[s]=size;}
namespace force{
bool vis[N], cge[N], mer[N], len;
void dfs(int u, int fa) {
//cout<<"dfs "<<u<<' '<<fa<<endl;
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
dfs(v, u);
if (e[i].val==1) {
if (vis[v]) continue;
else {
if (vis[u]==1) {continue;}
else {vis[u]=1; ++cnt[u];}
}
}
}
}
void solve() {
dfs(1, 0);
int ans=0;
for (int i=1; i<=n; ++i) ans+=cnt[i];
cout<<ans<<' '<<len<<endl;
exit(0);
}
}
namespace task1{
int v[N], cnt, len;
void solve() {
for (int i=1; i<n; ++i) v[i]=e[head[i]].val;
int lst=-1, clen=0;
bool con=0, other=0;
for (int i=1; i<=n; ++i) {
if (v[i]==1) {
if (v[i]!=lst) {
if (con) len+=clen+1, lst=v[i];
else ++cnt, ++len, lst=v[i];
}
else ++len;
other=0; con=0; clen=0;
}
else if (!v[i]) con=0, other=1, clen=0;
else {
if (lst==1 && !other) con=1, clen=1, other=0;
if (lst==2 && !other) ++clen;
if (lst==0) con=0, clen=0, other=1;
}
lst=v[i];
}
cout<<cnt<<' '<<len<<endl;
exit(0);
}
}
namespace task2{
struct sit{int cnt, len; inline void build(int c_, int l_) {cnt=c_; len=l_;}}dp[N][2];
void dfs(int u, int fa) {
int minn=INF, dlt=0;
int t1, t2;
// fa->0
int cnta1=0, cnta2=0, len1=0;
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
dfs(v, u);
if (e[i].val==1) {
++cnta1; cnta2+=dp[v][1].cnt; len1+=dp[v][1].len+1;
}
else if (!e[i].val) {
cnta2+=dp[v][0].cnt; len1+=dp[v][0].len;
}
}
dp[u][0].build(cnta2+(cnta1%2), len1);
// fa->1
int cntb1=1, cntb2=0, len2=0;
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
if (e[i].val==1) {
++cntb1; cntb2+=dp[v][1].cnt; len2+=dp[v][1].len+1;
}
else if (!e[i].val) {
cntb2+=dp[v][0].cnt; len2+=dp[v][0].len;
}
}
dp[u][1].build(cntb2+(cntb1%2), len2);
}
void solve() {
dfs(1, 0);
#if 1
cout<<"---dp---"<<endl;
for (int i=1; i<=n; ++i) cout<<dp[i][0].cnt<<' '<<dp[i][0].len<<' '<<dp[i][1].cnt<<' '<<dp[i][1].len<<endl;
cout<<"---dp---"<<endl;
#endif
printf("%d %d\n", dp[1][0].cnt/2, dp[1][0].len);
exit(0);
}
}
namespace task{
struct sit{int cnt, len; inline void build(int c_, int l_) {cnt=c_; len=l_;}}dp[N][2];
inline bool operator < (sit a, sit b) {return a.cnt==b.cnt?a.len<b.len:a.cnt<b.cnt;}
inline bool cmp(int cnta, int lena, int cntb, int lenb) {return cnta==cntb?lena<lenb:cnta<cntb;}
void dfs(int u, int fa) {
int cnta2=0, len1=0;
int cntb2=0, len2=0;
int cnt[2]={0, INF}, len[2]={0, INF};
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
dfs(v, u);
cnta2=cnt[0], len1=len[0];
cntb2=cnt[1], len2=len[1];
if (e[i].val==1) {
cnt[1]=cnta2+dp[v][1].cnt, len[1]=len1+dp[v][1].len+1;
cnt[0]=cntb2+dp[v][1].cnt, len[0]=len2+dp[v][1].len+1;
}
else if (!e[i].val) {
cnt[0]=cnta2+dp[v][0].cnt, len[0]=len1+dp[v][0].len;
cnt[1]=cntb2+dp[v][0].cnt, len[1]=len2+dp[v][0].len;
}
else {
if (cmp(cnta2+dp[v][0].cnt, len1+dp[v][0].len, cntb2+dp[v][1].cnt, len2+dp[v][1].len+1))
cnt[0]=cnta2+dp[v][0].cnt, len[0]=len1+dp[v][0].len;
else cnt[0]=cntb2+dp[v][1].cnt, len[0]=len2+dp[v][1].len+1;
if (cmp(cntb2+dp[v][0].cnt, len2+dp[v][0].len, cnta2+dp[v][1].cnt, len1+dp[v][1].len+1))
cnt[1]=cntb2+dp[v][0].cnt, len[1]=len2+dp[v][0].len;
else cnt[1]=cnta2+dp[v][1].cnt, len[1]=len1+dp[v][1].len+1;
}
}
//assert(cnt[0]==cnt[1]);
if (cmp(cnt[0], len[0], cnt[1]+1, len[1]))
dp[u][0].build(cnt[0], len[0]);
else dp[u][0].build(cnt[1]+1, len[1]);
if (cmp(cnt[0]+1, len[0], cnt[1], len[1]))
dp[u][1].build(cnt[0]+1, len[0]);
else dp[u][1].build(cnt[1], len[1]);
//dp[u][0].build(cnt[0], len[0]);
//dp[u][1].build(cnt[1]+1, len[1]);
}
void solve() {
dfs(1, 0);
#if 0
cout<<"---dp---"<<endl;
for (int i=1; i<=n; ++i) cout<<dp[i][0].cnt<<' '<<dp[i][0].len<<' '<<dp[i][1].cnt<<' '<<dp[i][1].len<<endl;
cout<<"---dp---"<<endl;
#endif
printf("%d %d\n", dp[1][0].cnt/2, dp[1][0].len);
exit(0);
}
}
signed main()
{
#ifdef DEBUG
freopen("1.in", "r", stdin);
#endif
bool flag=1, chain=1;
n=read();
for (int i=1,a,b,c,d; i<n; ++i) {
a=read(); b=read(); c=read(); d=read();
if (d==2) add(a, b, 2), add(b, a, 2);
else {add(a, b, c^d), add(b, a, c^d); flag=0;}
if (b!=a+1) chain=0;
}
//if (flag) {puts("0 0"); return 0;}
//if (chain) task1::solve();
//force::solve();
//task2::solve();
task::solve();
return 0;
}