Portal -->agc004F

Solution

   好神仙的转化qwq

​   首先我们可以先考虑\(m=n-1\)的情况下,也就是树的情况下要怎么做

   我们可以将这个问题转化一下:我们对这颗树重新染色,深度为奇数的点为黑色,深度为偶数的点为白色,这样一来原来的操作就变成了对两个相邻的颜色不同的节点进行颜色对调操作,最后的目的是要将所有的黑色点变成白色点,白色点变成黑色点

​   而每一次操作其实相当于交换两个点的颜色,所以我们可以得到结论:有解当且仅当白色点的数量和黑色点的数量相同

​   接下来为了更加直观,我们可以将这个过程理解成每次对调两个相邻的不同颜色的点的位置,使得用最少的步数做到将白点移到原来的黑点位置,黑点移到原来的白点位置

​   接下来我们从每条边的贡献来考虑:这条边连接的两个点需要被交换,说明原来子树中的一些点要被换出去,也就是说原来子树中的黑点比白点多或者白点比黑点多,而为了达到目的至少需要交换的次数应该是黑点和白点数量的差值

   然后为什么一定能够构造出一个这样的方案的话。。我不太会严谨证明只能感性理解qwq就是考虑每一次交换都会让差值减小\(1\),最后减到\(0\)就达到目的了

​   所以树的情况我们就直接求出每条边两个端点中较深的那个的子树内黑点和白点的差值的绝对值,然后累加起来就是答案了

  

​   接下来就是环的情况

   比较套路的处理方式是:既然我们已经知道树怎么做了,那我们考虑断掉一条边然后按照树的方法来算最后再将这条边的影响算上去

   因为黑色和白色跟深度有关,所以环这里需要根据奇偶性分两类讨论

​   首先是奇环的情况

​   因为是奇环,所以环中会出现两个同色的点相邻的情况,并且我们可以选择这两个颜色相同的点进行颜色反转操作,这对我们原来统计的影响是我们可以通过这条边将其中一个颜色的点数\(+1\)另一个颜色的点数\(-1\),也就是差值\(+2\)或者\(-2\),其他的情况都和树一致,也就是说差值不一定非零就无解,只要差值是一个偶数,我们就可以通过选择这条边进行操作最终将差值变成\(0\),然后我们断掉这条边,其他的按照树来处理就好了

  

​   然后是偶环

​   偶环的影响会更加复杂一点,与奇环不同,在偶环上操作并不会改变黑点总数或者白点总数,所以可以单纯地理解成将点移来移去,假设我们断掉的边是\((x,y)\),我们假设在最优方案下,这条边的使用次数是\(w\)(也就是将\(x\)子树中的\(w\)个点移到\(y\)子树内,\(w\)为负数表示从\(y\)移到\(x\)),那么也就是说,断掉\((x,y)\)之后的树中,记录\(x\)子树内的那条边的答案按照树的情况处理出来为\(w1\),那么实际上这条边的贡献应该是\(w1-w\),因为有\(w\)次需要分给\((x,y)\)这条边,而对于\(y\)这个点来说,对应的边贡献则是要\(+w\),因为从\(x\)那边多了\(w\)次转移到了\(y\)的子树中

​   但是现在的问题是,我们并不知道\(w\)是多少,这个时候我们将答案的表达式写出来可以得到:

\[ans=abs(w)+\sum\limits_{e}abs(f[e]+a\cdot w)
\]

​   其中\(f[e]\)表示的是按照树的方式算的每条边的贡献,\(a\)是一个系数,\(a\in \{-1,0,1\}\)

​   然后发现后面的形式可以看成在数轴上面给定若干个点,要确定一个点(也就是\(w\))使得所有的给定点到该点的距离之和最短,这个问题比较经典,答案应该是中位数

​   所以我们就可以直接将\(w\)求出来,然后计算\(ans\)啦

  

​   最后是一些实现上的问题:

1、虽然说上面的表达式中\(f\)是按照边存的,但是实际上在实现的时候按照点存会更加方便一点,\(f[i]\)表示的是\(i\)在树中从祖先过来的那条边的贡献

2、因为\(f[i]\)记录的子树内的信息,更新的时候是从后继的\(f\)值累加得到的,所以环情况中的影响需要沿着树中的祖先一路更新上去

   

​   代码大概长这个样子

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct xxx{
int y,nxt,x;
}a[N*2];
int h[N],dep[N],f[N],vis[N],pre[N],mark[N];
int rec[N],lis[N];
int n,m,tot,cutx,cuty,type,cut;
int ans;
int Abs(int x){return x<0?-x:x;}
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].x=x;}
void predfs(int fa,int x,int d){
int u;
dep[x]=d; f[x]=d%2==0?-1:1; pre[x]=fa;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
if (dep[u]){
cutx=x; cuty=u; cut=i;
type=((dep[x]+dep[u])%2)^1;
continue;
}
predfs(x,u,d+1);
f[x]+=f[u];
}
}
void solve(){
int tmp;
if (m==n-1){
if (f[1]){ans=-1; return;}
ans=0;
for (int i=1;i<=n;++i) ans+=Abs(f[i]);
return;
}
if (type){
if (f[1]%2){ans=-1; return;}
tmp=f[1]/2;
ans=Abs(tmp);
for (int i=cutx;i;i=pre[i]) f[i]-=tmp;
for (int i=cuty;i;i=pre[i]) f[i]-=tmp;
for (int i=1;i<=n;++i) ans+=Abs(f[i]);
return;
}
else{
if (f[1]){ans=-1; return;}
rec[0]=0;
memset(mark,0,sizeof(mark));
for (int i=cuty;i!=cutx&&i;i=pre[i])
rec[++rec[0]]=f[i],mark[i]=true;
sort(rec+1,rec+1+rec[0]);
if (rec[0]%2==0)
tmp=(rec[rec[0]/2]+rec[(rec[0]+1)/2])/2;
else
tmp=rec[(rec[0]+1)/2];
ans=Abs(tmp);
for (int i=1;i<=n;++i)
if (mark[i]) ans+=Abs(f[i]-tmp);
else ans+=Abs(f[i]);
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,tmp;
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
type=1;
predfs(0,1,1);
solve();
printf("%d\n",ans);
}

【agc004F】Namori的更多相关文章

  1. 【agc004f】Namori Grundy

    那个问一下有人可以解释以下这个做法嘛,看不太懂QwQ~ Description 有一个n个点n条边的有向图,点的编号为从1到n. 给出一个数组p,表明有(p1,1),(p2,2),…,(pn,n)这n ...

  2. 【ARC079F】Namori Grundy

    Description 题目链接 大意:给一张基环外向树.要求给每一个点确定一个值,其值为所有后继点的\(\text{mex}\).求是否存在确定权值方案. Solution 首先,对于叶子节点,其权 ...

  3. 【atcoder F - Namori】**

    F- Namori http://agc004.contest.atcoder.jp/tasks/agc004_f Time limit : 2sec / Memory limit : 256MB S ...

  4. Python高手之路【六】python基础之字符串格式化

    Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...

  5. 【原】谈谈对Objective-C中代理模式的误解

    [原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...

  6. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  7. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  8. 【调侃】IOC前世今生

    前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...

  9. Python高手之路【三】python基础之函数

    基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...

随机推荐

  1. Android 测试 之adb shell

    一.发送键盘事件: 命令格式1:adb shell input keyevent "value" 其中value以及对应的key code如下表所列: KeyEvent Value ...

  2. Jupyter 安装并配置工作路径[转]

    1.通过python的pip方式安装jupyterpython和pip都安装好后,通过cmd进入命令提示窗口,找到python安装目录下的Script目录,例如我的是路径是:C:\Program Fi ...

  3. Javac提示不是内部或外部命令

    1.先去百度搜索"jdk下载"下载最新版jdk,并安装,安装目录不用去更改,直接默认就好,下载完了之后,双击打开安装,jdk安装完成后,会接着安装jre包,(jre和jdk是配对的 ...

  4. 【RL系列】On-Policy与Off-Policy

    强化学习大致上可分为两类,一类是Markov Decision Learning,另一类是与之相对的Model Free Learning 分为这两类是站在问题描述的角度上考虑的.同样在解决方案上存在 ...

  5. C# 反射,动态编译

    反射是动态获取程序集的元数据的一种技术,这句话是做.NET程序员面试题目的一个的答案,你可选择记住它,就好比高中生物学里面讲到的细胞的结构的课程时,细胞由细胞膜,细胞质和细胞核组成.根据做程序的经验, ...

  6. JS中自定义事件的使用与触发

    1. 事件的创建 JS中,最简单的创建事件方法,是使用Event构造器: var myEvent = new Event('event_name'); 但是为了能够传递数据,就需要使用 CustomE ...

  7. [leetcode-908-Smallest Range I]

    Given an array A of integers, for each integer A[i] we may choose any x with -K <= x <= K, and ...

  8. Scrum立会报告+燃尽图(十月二十五日总第十六次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284 项目地址:https://git.coding.net/zhang ...

  9. Beta周王者荣耀交流协会第六次会议

    1.立会照片 成员王超,高远博,冉华,王磊,王玉玲,任思佳,袁玥全部到齐. master:袁玥 2. 时间跨度 2017年11月15日 19:00 — 19:10 ,总计10分钟. 3. 地点 一食堂 ...

  10. TCP系列45—拥塞控制—8、SACK关闭的拥塞撤销与虚假快速重传

    一.概述 这篇文章介绍一下TCP从Recovery状态恢复到Open状态的时候cwnd的更新.我们在tcp重传部分的文章中曾经介绍过虚假重传的概念,Linux在探测到虚假重传的时候就会执行拥塞撤销操作 ...