正题
题目大意
nnn个点的一棵树,每条边一个权值为0或1和一个目标权值(0或1或者没有限制)。每次可以将一个路径上的权值取反,求最小翻转数量和最小翻转路径长度。
解题思路
首先我们可以从序列的类似问题上知道一条边不会被翻转超过一次,所以问题变为了求最少的路径覆盖所有需要翻转的点。考虑如何dpdpdp,我们知道一个点如果被奇数条需要翻转的边相连,那么该点必定需要作为其中一条路径的起点,那么需要翻转的数量就是奇数入度点的数量除以2
设fi,0/1f_{i,0/1}fi,0/1表示dpdpdp到iii点时,该点连向父节点的翻转或者不翻转(如果有必定的要求就让另一个dpdpdp值为0)时的答案二元组,然后进行dpdpdp即可
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,inf=2147483647/3;
struct node{int to,next,w;
}a[N*2];
int n,tot,ls[N];
pair<int,int> f[N][2];
pair<int,int> operator +(pair<int,int> x,pair<int,int> y)
{return make_pair(x.first+y.first,x.second+y.second);}
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
void dfs(int x,int fa,int k){pair<int,int> f0(0,0),f1(inf,inf);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;dfs(y,x,a[i].w);pair<int,int> l=f0,r=f1;f0=min(l+f[y][0],r+f[y][1]);f1=min(r+f[y][0],l+f[y][1]);}if(k==0||k>=2)f[x][0]=min(f0,f1+make_pair(1,0));if(k==1||k>=2)f[x][1]=min(f0+make_pair(1,1),f1+make_pair(0,1));return;
}
int main()
{int size = 256 << 20; //250Mchar*p=(char*)malloc(size) + size;__asm__("movl %0, %%esp\n" :: "r"(p) );scanf("%d",&n);for(int i=1;i<n;i++){int x,y,w,d;scanf("%d%d%d%d",&x,&y,&w,&d);addl(x,y,w^d);addl(y,x,w^d);}memset(f,0x3f,sizeof(f));dfs(1,1,0);printf("%d %d",f[1][0].first/2,f[1][0].second);
}