题意:
给你一棵树或者一颗环套树,节点初始全为白色,每次可以选择一对相邻的相同颜色的节点翻转他们的颜色,问能不能通过操作使得所有结点变成黑色,输出最小步数,若不能则输出-1。
$2leq Nleq 10^5$
$N-1leq Mleq N$
对于40%的数据有$M=N-1$
题解:
这个模型转化真是神仙。。。真的是所谓的不可做题啊。。。
对于树
把树上的结点按照深度奇偶分类,那么我们喜闻乐见的发现这是一个二分图。设深度为奇数的点上有一个球,偶数的点上有一个坑(sk原话),那么一次操作相当于把一个球放到一个相邻的坑里(相互配对),那么最终的情况就是原本的坑都被球填满。显然只有坑数等于球数才有解。
考虑以一个点为根的子树,它子树中至少要经过这个点的操作数(即放进或扌商出的球数)为这颗子树中球数与坑数差的绝对值,形式化地,设球点权值为1,坑权值为-1,子树和为$sum_u$,那么答案$ans=sumlimits_{i=1}^{n}abs(sum_i)$。
考虑能否达到这个下界,构造一下,把每个点的儿子分为放进和扌商出两种,每次将进和出的相互配对,多余的再向上移动,即可达到下界。
对于环套树
因为奇环偶环操作时奇偶性不同,所以要分别考虑;
对于奇环
把环上多出来的任意一条边拆掉,转化成树,那么边的端点$u$,$v$一定在二分图的同一边,考虑这条边操作对答案的影响,一定是在$u$和$v$同时加或减一个球。那么求出拆成树之后差多少个球或者坑,如果是奇数显然无解,否则把影响加到$u$,$v$到根的链上,再用树的做法统计一次即可。
对于偶环
类似上面的,由于是偶环,$u$和$v$不在同一边,设对这条边操作了$k$次(k可以小于0),则$u$上加了$k$个球,$v$上减了$k$个球;枚举包含这条边影响的子树,设他们$k$的系数为$x_i$,则显然只有0,1,-1三种情况,那么答案$ans=abs(k)+sumlimits_{i=1}^{n}abs(sum_i+x_ik)$;
其中系数为0的部分没有影响,可以直接加入答案,剩下的可以看成数轴上取数使得距离其它点距离最小,显然中位数最优。
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define DCSB {printf("-1");exit(0);}
7 using namespace std;
8 struct edge{
9 int v,next;
10 }a[500001];
11 int n,m,u,v,ans=0,els,huanu,huanv,tot=0,q[500001],head[500001],fa[500001],dep[500001],f[500001];
12 void add(int u,int v){
13 a[++tot].v=v;
14 a[tot].next=head[u];
15 head[u]=tot;
16 }
17 void dfs(int u,int ff,int dpt){
18 dep[u]=dpt;
19 fa[u]=ff;
20 f[u]=(dpt%2)?1:-1;
21 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
22 int v=a[tmp].v;
23 if(v!=ff){
24 if(dep[v]){
25 huanu=u;
26 huanv=v;
27 continue;
28 }
29 dfs(v,u,dpt+1);
30 f[u]+=f[v];
31 }
32 }
33 }
34 void gao_tree(){
35 if(f[1]!=0)DCSB
36 for(int i=1;i<=n;i++)ans+=abs(f[i]);
37 printf("%d",ans);
38 }
39 void gao_jihuan(){
40 if(f[1]%2!=0)DCSB
41 els=-f[1]/2;
42 ans=abs(els);
43 for(;huanu;huanu=fa[huanu])f[huanu]+=els;
44 for(;huanv;huanv=fa[huanv])f[huanv]+=els;
45 for(int i=1;i<=n;i++)ans+=abs(f[i]);
46 printf("%d",ans);
47 }
48 void gao_ouhuan(){
49 int top,mid;
50 top=0;
51 if(f[1])DCSB
52 for(;huanv!=huanu;huanv=fa[huanv]){
53 q[++top]=f[huanv];
54 dep[huanv]=0;
55 }
56 sort(q+1,q+top+1);
57 mid=q[(top+1)/2];
58 ans=abs(mid);
59 for(int i=1;i<=n;i++)if(dep[i])ans+=abs(f[i]);
60 for(int i=1;i<=top;i++)ans+=abs(q[i]-mid);
61 printf("%d",ans);
62 }
63 int main(){
64 memset(head,-1,sizeof(head));
65 scanf("%d%d",&n,&m);
66 for(int i=1;i<=m;i++){
67 scanf("%d%d",&u,&v);
68 add(u,v);
69 add(v,u);
70 }
71 dfs(1,0,1);
72 if(m==n-1)gao_tree();
73 else if((dep[huanv]-dep[huanu])%2==0)gao_jihuan();
74 else gao_ouhuan();
75 return 0;
76 }