zoukankan      html  css  js  c++  java
  • [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 }
  • 相关阅读:
    【译文】HTML5 Canvas的点击区域检测以及如何监听Canvas上各种图形的点击事件
    快速排序法精简理解
    数据结构--堆
    数据结构--哈希表
    数据结构--队列
    数据结构--栈
    数据结构--数组
    数据结构--链表
    算法的基本知识
    Arthas
  • 原文地址:https://www.cnblogs.com/dcdcbigbig/p/9508442.html
Copyright © 2011-2022 走看看