传送门


因为不会列方程然后只会树上的,被吊打了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. SSIS 包部署错误 0xC0010014

    SSIS 包部署错误 0xC0010014 Reinhard 在部署 SSIS 包时,提示如下错误. 由于错误 0xC0010014"发生了一个或多个错误.在此消息之前应有更为具体的错误消息 ...

  2. 切换横竖屏的时候Activity的生命周期变化情况

    关于这个,有个博客说得比较清楚:http://blog.csdn.net/wulianghuan/article/details/8603982,直接给出链接,哈哈哈.

  3. Elasticsearch Query DSL

    Elasticsearch Query DSL By:授客 QQ:1033553122 1. match_all 1 2. match 2 3. match_phrase 5 4. match_phr ...

  4. celery任务进程关闭

    方法1: ps auxww|grep 方法2: Ctrl+C 方法3: celery multi 管理 celery multi start w1 -A proj -l info celery mul ...

  5. Proxysql读写分离配置

    ProxySQL是Percona主推的读写分离中间件,下载地址为: https://www.percona.com/downloads/proxysql/ 一.安装 1:下载 wget https:/ ...

  6. SSM框架—环境搭建(MyEclipse+Tomcat+MAVEN+SVN)

    1.JDK的安装 首先下载JDK,这个从sun公司官网可以下载,根据自己的系统选择64位还是32位,安装过程就是next一路到底.安装完成之后当然要配置环境变量了. 1.1新建变量名:JAVA_HOM ...

  7. NPOI导入导出Excel工具类

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Ref ...

  8. Linux CFS调度器之队列操作--Linux进程的管理与调度(二十七)

    1. CFS进程入队和出队 完全公平调度器CFS中有两个函数可用来增删队列的成员:enqueue_task_fair和dequeue_task_fair分别用来向CFS就绪队列中添加或者删除进程 2 ...

  9. c/c++ 多态的实现原理分析

    多态的实现原理分析 当类里有一个函数被声明成虚函数后,创建这个类的对象的时候,就会自动加入一个__vfptr的指针, __vfptr维护虚函数列表.如果有三个虚函数,则__vfptr指向的是第一个虚函 ...

  10. Zabbix WMI监控

    检查Windows OS是否激活,5表示处于通知模式,1表示已激活 wmi.get[root\cimv2,select LicenseStatus FROM SoftwareLicensingProd ...