[agc004f]namori

题意:

给你一棵树或者一颗环套树,节点初始全为白色,每次可以选择一对相邻的相同颜色的节点翻转他们的颜色,问能不能通过操作使得所有结点变成黑色,输出最小步数,若不能则输出-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 }
原文地址:https://www.cnblogs.com/dcdcbigbig/p/9508442.html