传送门


因为不会列方程然后只会树上的,被吊打了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. Spring学习之旅(五)极速创建Spring AOP java工程项目

    编译工具:eclipse. 简单说一下,Spring  AOP是干嘛的? 假设你创建了一群类:类A,类B,类C,类D.... 现在你想为每个类都增加一个新功能,那么该怎么办呢?是不是想到了为每个类增加 ...

  2. Android JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...

  3. ionic提示弹框

    //提示框 .factory('TipsPort', function ($ionicPopup) { var TipsPort = function (tipsText, SureFunction, ...

  4. fetch数据请求的封装

    export default class HttpUtils { static get(url){ return new Promise((resolve,reject)=>{ fetch(ur ...

  5. css和HTML布局小技巧

    一:水平居中 1. 如下所示,让child在parent中水平居中 <!DOCTYPE html> <html> <head lang="en"> ...

  6. (网页)sweetalert api 中文开发文档和手册,项目放弃alert

    弹框json的特别好使. sweetalert 示例 基本信息弹窗swal("这是一条信息!") 标题与文本的信息弹窗swal("这是一条信息!", " ...

  7. php get接口,并在浏览器中以json格式返回查找到的数据

    php查询数据有6个步骤,分别为: 连接数据库服务器,使用的命令为:mysql_connect("服务器名称","用户名","密码") 选择 ...

  8. matlab练习程序(点云表面法向量)

    思路还是很容易想到的: 1.首先使用KD树寻找当前点邻域的N个点,这里取了10个,直接调用了vlfeat. 2.用最小二乘估计当前邻域点组成的平面,得到法向量. 3.根据当前邻域点平均值确定邻域质心, ...

  9. [20181007]12cR2 Using SQL Patch.txt

    [20181007]12cR2 Using SQL Patch.txt --//12cR2 已经把sql打补丁集成进入dbms_sqldiag,不是11g的 DBMS_SQLDIAG_INTERNAL ...

  10. Python学习手记

    1.Python大小敏感.print写作PRINT或Print是不对.2.注释符是“#”,而非“//”.3.语句结尾不必须分号.4.转义符为“/”+转义字母.这点与刀莱特一致.5.单引号输入使用“/' ...