AGC004F Namori 树形DP、解方程(?)
因为不会列方程然后只会树上的,被吊打了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、解方程(?)的更多相关文章
- 深探树形dp
看到同学在写一道树形dp,好奇直接拿来写,发现很不简单. 如图,看上去是不是很像选课,没错这不是选课,升级版吧,多加了点东西罢了.简单却调了一晚上和一上午. 思路:很简单强联通分量+缩点+树形dp.直 ...
- 洛谷AT2046 Namori(思维,基环树,树形DP)
洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...
- 2017广东工业大学程序设计竞赛决赛 题解&源码(A,数学解方程,B,贪心博弈,C,递归,D,水,E,贪心,面试题,F,贪心,枚举,LCA,G,dp,记忆化搜索,H,思维题)
心得: 这比赛真的是不要不要的,pending了一下午,也不知道对错,直接做过去就是了,也没有管太多! Problem A: 两只老虎 Description 来,我们先来放松下,听听儿歌,一起“唱” ...
- 【动态规划】树形DP完全详解!
蒟蒻大佬时隔三个月更新了!!拍手拍手 而且是更新了几篇关于DP的文章(RioTian狂喜) 现在赶紧学习和复习一下树形DP.... 树形DP基础:Here,CF上部分树形DP练习题:Here \[QA ...
- 树形DP详解+题目
关于树形dp 我觉得他和线性dp差不多 总结 最近写了好多树形dp+树形结构的题目,这些题目变化多样能与多种算法结合,但还是有好多规律可以找的. 先说总的规律吧! 一般来说树形dp在设状态转移方程时都 ...
- 树形DP入门详解+题目推荐
树形DP.这是个什么东西?为什么叫这个名字?跟其他DP有什么区别? 相信很多初学者在刚刚接触一种新思想的时候都会有这种问题. 没错,树形DP准确的说是一种DP的思想,将DP建立在树状结构的基础上. 既 ...
- 洛谷 P2279 [HNOI2003]消防局的设立 (树形dp or 贪心)
一看到这道题就知道是树形dp 之前做过类似的题,只不过保护的范围是1 所以简单很多. 这道题保护的范围是2,就复杂了很多. 我就开始列状态,然后发现竟然有5种 然后我就开始列方程. 但是我考虑的时候是 ...
- 【NOIP2016提高A组集训第14场11.12】随机游走——期望+树形DP
好久没有写过题解了--现在感觉以前的题解弱爆了,还有这么多访问量-- 没有考虑别人的感受,没有放描述.代码,题解也写得歪歪扭扭. 并且我要强烈谴责某些写题解的代码不打注释的人,像天书那样,不是写给普通 ...
- 【BZOJ-1131】Sta 树形DP
1131: [POI2008]Sta Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1150 Solved: 378[Submit][Status] ...
随机推荐
- Python-Django 整合Django和jquery-easyui
整合Django和jquery-easyui by:授客 QQ:1033553122 测试环境 win7 64 Python 3.4.0 jquery-easyui-1.5.1 下载地址1:http: ...
- Android为TV端助力 apk静默安装
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149 之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢 ...
- 【LeetCode】两数相加
题目描述 给出两个非空的链表用来表示两个非负的整数.其中,它们各自的位数是按照逆序的方式存储的,并且它们的每个节点只能存储一位数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和. ...
- Anaconda管理Python环境
Anaconda介绍 Anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依赖项.Anaconda提供环境管理的功能,功能类似 Virtuale ...
- DAY14(PYTHONS)生成器进阶
def average(): sum = 0 count = 0 avg = 0 while True: #循环 num = yield avg #遇到yield就停止,防止一开始除数为0导致崩溃 s ...
- spring-AOP(面向切面编程)-xml方式配置
AOP是针对面向对象编程的一种补充,有时使用面向对象不能很好完成一些额外的功能业务时,可以采用AOP来进行补充. AOP术语: 切面(Aspect) 切面是用于编写切面逻辑的一个类,这个类很类似于JD ...
- java POI导出Excel文件数据库的数据
在web开发中,有一个经典的功能,就是数据的导入导出.特别是数据的导出,在生产管理或者财务系统中用的非常普遍,因为这些系统经常要做一些报表打印的工作.这里我简单实现导出Excel文件. POI jar ...
- Windows Server 2016-WinSer 2016标准版与数据中心版的区别
今天在整理文章的时候看到有读者问到他现在的测试环境是用的Windows Server 2016标准版,和我现阶段系列文章的环境是否有区别. 其实针对Windows Server 2016 Active ...
- Lua不显示小数点0的部分
我的环境:Unity3D 5.3.7p4 XLua版本v2.1.6 基于Lua5.3 (https://github.com/Tencent/xLua) 在Lua中数字不区分整型或浮点型,所有都是nu ...
- 聚类——GAKFCM
聚类——GAKFCM 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 参考文献:黄白梅. 基于GA优化的核模糊C均值聚类算法的研究[D]. 武汉科技大学 ...