传送门


因为不会列方程然后只会树上的,被吊打了QAQ

不难想到从叶子节点往上计算答案。可以考虑到可能树上存在一个点,在它的儿子做完之后接着若干颜色为白色的儿子,而当前点为白色,只能帮助一个儿子变成黑色,所以需要寻求父亲的帮助,强制让父亲变为黑色若干次,然后将当前点和父亲同时反转成白色,然后将这个点和儿子一起反转成黑色。

所以设\(f_i\)表示\(i\)强制被染成黑色的次数,若\(f_i < 0\)表示要被强制染成白色\(-f_i\)次,转移:\(f_i = 1 - \sum\limits_{u \in son_i} f_u\)(因为儿子强制染成白色若干次就是其父亲强制染成黑色这么多次然后将它和儿子一起反转,反之亦然),最后若\(f_1 \neq 0\)则无解,否则答案为\(\sum\limits_{i=1}^N f_i\)。

考虑基环树上怎么做。先把树上的环搜出来,对于环上每一个点的子树做树算法,那么环上的每一个点都有强制变为黑色/白色若干次的限制。

这个时候没有“父亲”的概念了,现在所有可利用的边都是让两个点同时变黑一次或者变白一次。所以设\(a_i\)表示连接环上第\(i\)个点和第\(i+1\)个点的边两边的点同时从白色变为黑色的次数,可以得到\(l\)个方程(\(l\)为环长):

\(\begin{align*} a_1 + a_2 = f_2 & (1) \\ a_2 + a_3 = f_3 & (2) \\ ... \\ a_1 + a_l = f_l & (l) \end{align*}\)

不难知道:所有\(a_i\)在且仅在所有式子中出现\(2\)次。那么我们可以将所有式子相加可得

\(\sum\limits_{i = 1} ^ l a_i = \frac{\sum\limits_{i=1}^l f_i}{2}(*)\)

当然\(2 \not\mid \sum f_i\)时无解。

如果\(2 \not\mid l\),意味着接下来\((*) - (1) - (3) - ... - (l-2)\)可以得到\(a_l\),进而推知所有\(a_i\)的值。但是\(2 \mid l\)时就没有这个性质了,因为\((l)\)式的左边可以由\((l-1)-(l-2)+(l-3)-...- (2) + (1)\)得到,所以有一个自由元。不妨设自由元为\(a_l\),那么所有\(a_i\)都可以表示成\(val_i \pm a_l\)的形式。我们要最小化的是\(\sum\limits_{i=1}^N |val_i \pm a_l|\),即\(\sum\limits_{i=1}^N |a_l \pm val_i|\),由小学奥数取中位数时有最小值。

注意一个判断无解的地方:当图为基环树且\(2 \mid l\)时,\((l) = (l-1)-(l-2)+(l-3)- ... - (2) + (1)\),如果不相等也是无解。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std; inline int read(){
int a = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return a;
} const int MAXN = 1e5 + 7;
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
int head[MAXN] , N , M , cntEd;
bool vis[MAXN]; inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
} int ans; int dfs(int x){
vis[x] = 1;
int col = 0 , more = 0;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
int t = dfs(Ed[i].end);
if(t > 0) more += t;
else col -= t;
}
ans += more + col;
return col - more - 1;
} namespace solveTree{
void main(){
if(dfs(1))
cout << "-1";
else
cout << ans;
}
} namespace solveCir{
vector < int > cir , val;
int find(int x , int p){
vis[x] = 1;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != p)
if(vis[Ed[i].end]){
cir.push_back(x);
return Ed[i].end;
}
else{
int t = find(Ed[i].end , x);
if(t){
cir.push_back(x);
return x == t ? 0 : t;
}
}
vis[x] = 0;
return 0;
} inline int abss(int x){return x < 0 ? -x : x;} void main(){
find(1 , 0);
for(auto t : cir)
val.push_back(-dfs(t));
int sum = 0;
for(auto t : val)
sum += t;
if(sum & 1){puts("-1"); return;}
sum >>= 1;
if(cir.size() & 1){
for(int i = 0 ; i + 1 < cir.size() ; i += 2)
sum -= val[i];
ans += abss(sum);
for(int i = (int)val.size() - 2 ; i >= 0 ; --i){
sum = val[i] - sum;
ans += abss(sum);
}
}
else{
int cur = 0;
for(auto t : val)
cur = t - cur;
if(cur){puts("-1"); return;}
vector < int > now;
now.push_back(0);
cur = 0;
bool f = 0;
for(int i = (int)val.size() - 2 ; i >= 0 ; --i){
cur = val[i] - cur;
now.push_back(f ? -cur : cur);
f ^= 1;
}
sort(now.begin() , now.end());
int mid = now[((int)now.size() - 1) >> 1];
for(auto t : now)
ans += abss(mid - t);
}
cout << ans;
}
} signed main() {
N = read();
M = read();
for(int i = 1 ; i <= M ; ++i){
int a = read() , b = read();
addEd(a , b);
addEd(b , a);
}
if(M == N - 1)
solveTree::main();
else
solveCir::main();
return 0;
}

AGC004F Namori 树形DP、解方程(?)的更多相关文章

  1. 深探树形dp

    看到同学在写一道树形dp,好奇直接拿来写,发现很不简单. 如图,看上去是不是很像选课,没错这不是选课,升级版吧,多加了点东西罢了.简单却调了一晚上和一上午. 思路:很简单强联通分量+缩点+树形dp.直 ...

  2. 洛谷AT2046 Namori(思维,基环树,树形DP)

    洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...

  3. 2017广东工业大学程序设计竞赛决赛 题解&源码(A,数学解方程,B,贪心博弈,C,递归,D,水,E,贪心,面试题,F,贪心,枚举,LCA,G,dp,记忆化搜索,H,思维题)

    心得: 这比赛真的是不要不要的,pending了一下午,也不知道对错,直接做过去就是了,也没有管太多! Problem A: 两只老虎 Description 来,我们先来放松下,听听儿歌,一起“唱” ...

  4. 【动态规划】树形DP完全详解!

    蒟蒻大佬时隔三个月更新了!!拍手拍手 而且是更新了几篇关于DP的文章(RioTian狂喜) 现在赶紧学习和复习一下树形DP.... 树形DP基础:Here,CF上部分树形DP练习题:Here \[QA ...

  5. 树形DP详解+题目

    关于树形dp 我觉得他和线性dp差不多 总结 最近写了好多树形dp+树形结构的题目,这些题目变化多样能与多种算法结合,但还是有好多规律可以找的. 先说总的规律吧! 一般来说树形dp在设状态转移方程时都 ...

  6. 树形DP入门详解+题目推荐

    树形DP.这是个什么东西?为什么叫这个名字?跟其他DP有什么区别? 相信很多初学者在刚刚接触一种新思想的时候都会有这种问题. 没错,树形DP准确的说是一种DP的思想,将DP建立在树状结构的基础上. 既 ...

  7. 洛谷 P2279 [HNOI2003]消防局的设立 (树形dp or 贪心)

    一看到这道题就知道是树形dp 之前做过类似的题,只不过保护的范围是1 所以简单很多. 这道题保护的范围是2,就复杂了很多. 我就开始列状态,然后发现竟然有5种 然后我就开始列方程. 但是我考虑的时候是 ...

  8. 【NOIP2016提高A组集训第14场11.12】随机游走——期望+树形DP

    好久没有写过题解了--现在感觉以前的题解弱爆了,还有这么多访问量-- 没有考虑别人的感受,没有放描述.代码,题解也写得歪歪扭扭. 并且我要强烈谴责某些写题解的代码不打注释的人,像天书那样,不是写给普通 ...

  9. 【BZOJ-1131】Sta 树形DP

    1131: [POI2008]Sta Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1150  Solved: 378[Submit][Status] ...

随机推荐

  1. python+selenium+PhantomJS爬取网页动态加载内容

    一般我们使用python的第三方库requests及框架scrapy来爬取网上的资源,但是设计javascript渲染的页面却不能抓取,此时,我们使用web自动化测试化工具Selenium+无界面浏览 ...

  2. vue 构建项目 文件引入

    1.vue引用依赖文件.  举例:axios 先安装 axios.如果直接安装 vue-axios 会报错 npm install axios npm install --save axios vue ...

  3. 在a标签内添加hover样式的方法:

    <a href="javascript:void(0);" onmouseover="this.style.color='yellow';" onmous ...

  4. Struts2之action 之 感叹号 ! 动态方法调用

    struts2的动态方法调用的方式: 1.第一种方式:设置method属性 在Action类中定义一个签名与execute方法相同.只是名字不同的方法,如定义为: public String logi ...

  5. (三)版本控制管理器之CVS(下)

    在上一篇文章<(二)版本控制管理器之CVS(上)>中,我为大家介绍了什么是CVS.CVS的特点.CVS的安装.CVSNT服务器的配置.TortoiseCVS客户端的配置等,本篇文章继续为大 ...

  6. 如何在数据表当中找出被删掉的数据行ID

    这个问题是一年前我刚步入IT行业的一个面试题,当时抓破头皮都想不到的问题,但现在回想过去自身不禁感到可笑,不多扯直接写解决方案.如何在数据表当中找出被删掉的数据行ID,意思是:在一堆的数据当中,让你找 ...

  7. 如何定位那些SQL产生了大量的redo日志

    在ORACLE数据库的管理.维护过程中,偶尔会遇到归档日志暴增的情况,也就是说一些SQL语句产生了大量的redo log,那么如何跟踪.定位哪些SQL语句生成了大量的redo log日志呢? 下面这篇 ...

  8. [20190214]11g Query Result Cache RC Latches.txt

    [20190214]11g Query Result Cache RC Latches.txt --//昨天我重复链接http://www.pythian.com/blog/oracle-11g-qu ...

  9. CentOS 6.5 搭建 .NET 环境, Mono 5.16.0 + Jexus 5.8

    最近有这样一个打算,就是准备把以前的有一个.NET 网站部署在Linux 下面,正好试试 .NET 跨平台的功能,为后续研究 .netCore 方向准备. 搭建环境: CentOS 6.5 + Mon ...

  10. redmine screenshot paste(粘贴截图)

    前言 本文所使用的 redmine 3.1.x 查看redmine版本:以管理员帐号登录 – 点 管理 – 点 信息,示例如下: 在线插件库 插件仓库,可以下载丰富的插件: http://www.re ...