【agc004F】Namori
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\)是多少,这个时候我们将答案的表达式写出来可以得到:
\]
其中\(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的更多相关文章
- 【agc004f】Namori Grundy
那个问一下有人可以解释以下这个做法嘛,看不太懂QwQ~ Description 有一个n个点n条边的有向图,点的编号为从1到n. 给出一个数组p,表明有(p1,1),(p2,2),…,(pn,n)这n ...
- 【ARC079F】Namori Grundy
Description 题目链接 大意:给一张基环外向树.要求给每一个点确定一个值,其值为所有后继点的\(\text{mex}\).求是否存在确定权值方案. Solution 首先,对于叶子节点,其权 ...
- 【atcoder F - Namori】**
F- Namori http://agc004.contest.atcoder.jp/tasks/agc004_f Time limit : 2sec / Memory limit : 256MB S ...
- Python高手之路【六】python基础之字符串格式化
Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...
- 【原】谈谈对Objective-C中代理模式的误解
[原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【调侃】IOC前世今生
前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...
- Python高手之路【三】python基础之函数
基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...
随机推荐
- Python数据结构 将列表作为栈和队列使用
列表作为栈使用 Python列表方法使得列表作为堆栈非常容易,最后一个插入,最先取出(“后进先出”).要添加一个元素到堆栈的顶端,使用 append() .要从堆栈顶部取出一个元素,使用 pop() ...
- 剑指offer-二维数组中的查找01
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- 算法笔记(c++)--求一个数的所有质数因子
算法笔记(c++)--求一个数的所有质数因子 先贴题目: 这题不难,恶心在理解上面.最后看评论知道了怎么回事: 2*2*3*3*5=180 按照这逻辑的话应该输入的数由一系列质数相乘出来,所以每次找到 ...
- 王者荣耀交流协会第二次Scrum立会
会议时间:2017年10月21号 17:00-17:22,时长22分钟. 会议地点:首尔名家里面的大桌子(PS:感谢组长大大请我们吃饭~)立会内容:每位同学汇报了今日工作(高远博与王超同学在今日有 ...
- 【IdentityServer4文档】- 术语&演示服务器和测试
术语 你需要了解一下,规范.文档和对象模型使用的术语有哪些. IdentityServer IdentityServer 是一个 OpenID Connect 提供程序 - 它实现了 OpenID C ...
- 【并查集】 不相交集合 - 并查集 教程(文章作者:Slyar)
最近写了一个多星期的并查集,一瞬间贴出这么多解题报告,我想关于并查集的应用先告一段落吧,先总结一下. 在网上看到一篇关于并查集比较好的教程(姑且允许我这么说吧),不转过来是在可惜.献给爱学习的你 文章 ...
- Navicat for mysql导入.sql数据库大小受限制
把导入单个表的最大限制调一下就行(在my.ini里面就算改了max_allowed_packet也不一定行,因为Navicat貌似并不调用,实际他有自己的一套默认配置,所以需要在Navicat上调整) ...
- [pascal入门]数组
一.本节目标 本节我们将要讲述数组.本节目标: 一维数组 二维数组 字符数组 二.一维数组 我们通过一个案例来简单的理解数组.班主任要计算班级里面50个同学数学成绩的平均成绩,道理上讲这是一个比较简单 ...
- 配置java环境 启动服务
1:查看当前的Java JDK版本,是否符合要求,下载的为2.4.4版本,因此满足条件 [root@7 ~]# java -version openjdk version "1.8.0_65 ...
- 软件工程个人作业3——集大通APP案例分析
第一部分:调研, 评测 1.第一次上手体验 主要界面截图: 感受: 1.界面不美观: 2.特色功能展现模块不突出,以上截图为打开APP所看到的界面展示,但是这些功能都不是该APP的特色功能,显得有些累 ...