DTOJ #3316. baka

【题目描述】

给定一颗有方向的树,改变一条边方向的代价为 $1$,求使得从两个点出发能到达树上所有节点的最小代价。

【输入格式】

第一行一个正整数 $n$。

接下来 $n-1$ 行,每行两个正整数 $u,v$,表示 $u$ 向 $v$ 连一条有向边。

【输出格式】

一行一个数表示答案。

【样例】

样例输入
4
2 1
3 1
4 1

样例输出
1
【数据范围与提示】

对于 $100\%$ 的数据,$1\leq n\leq 10^6]$。

【题解】

本题有两种思路。

第一种,考虑到有两个起点,设 $f[i][1/2][0/1]$ 表示以 $i$ 为根的子树内已经有 $1/2$ 个出发点,$i$ 是否为出发点,直接转移即可。

第二种,考虑到两个起点必然将整棵树分成两部分,考虑断边,换根 $dp$ 即可。

【代码】

#include<bits/stdc++.h>
const int maxn=1000000+10;
const int inf=1000000000;
int n,f[maxn][2][2],g[2][2];
std::vector<int> E[maxn],G[maxn];
inline int read ( void )
{
    int x=0;char ch;bool f=true;
    while ( !isdigit(ch=getchar()) ) if ( ch==‘-‘ ) f=false;
    for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
    return f ? x : -x ;
}
using std::min;
inline void dfs ( int u,int fr )
{
    f[u][0][0]=f[u][1][0]=f[u][1][1]=inf;
    for ( int v:E[u] ) if ( v!=fr )
    {
        dfs(v,u);
        for ( int i=0;i<2;i++ ) for ( int j=0;j<2;j++ ) g[i][j]=f[u][i][j];
        f[u][0][0]=min(g[0][0]+f[v][0][1],g[0][1]+min(f[v][0][0],f[v][0][1])+1);
        f[u][0][1]=g[0][1]+f[v][0][1];
        f[u][1][0]=min(min(g[0][0]+min(min(f[v][0][0],f[v][1][1]),f[v][0][1]+1),g[0][1]+min(f[v][1][0],f[v][1][1])+1),min(g[1][0]+f[v][0][1],g[1][1]+min(f[v][0][0],f[v][0][1])+1));
        f[u][1][1]=min(g[1][1]+f[v][0][1],g[0][1]+min(f[v][1][1],f[v][0][0]));
    }
    for ( int v:G[u] ) if ( v!=fr )
    {
        dfs(v,u);
        for ( int i=0;i<2;i++ ) for ( int j=0;j<2;j++ ) g[i][j]=f[u][i][j];
        f[u][0][0]=min(g[0][0]+f[v][0][1]+1,g[0][1]+min(f[v][0][0],f[v][0][1]));
        f[u][0][1]=g[0][1]+f[v][0][1]+1;
        f[u][1][0]=min(min(g[0][0]+min(min(f[v][0][0],f[v][1][1]+1),f[v][0][1]+0),g[0][1]+min(f[v][1][0],f[v][1][1])+0),min(g[1][0]+f[v][0][1]+1,g[1][1]+min(f[v][0][0],f[v][0][1])));
        f[u][1][1]=min(g[1][1]+f[v][0][1],g[0][1]+min(f[v][1][1],f[v][0][0]))+1;
    }
}
signed main()
{
    n=read();
    for ( int i=1,u,v;i<n;i++ ) u=read(),v=read(),E[u].push_back(v),G[v].push_back(u);
    dfs(1,0);
    return !printf("%d\n",min(min(f[1][0][0],f[1][0][1]),min(f[1][1][0],f[1][1][1])));
}

原文地址:https://www.cnblogs.com/RenSheYu/p/11330275.html

时间: 2024-10-23 18:48:48

DTOJ #3316. baka的相关文章

#3316. baka

题目描述 Worldwide_D最近沉迷东方. 数学课上,Worldwide_D在研究对数,然后渐渐睡着了.他梦见自己躺在幻想乡的雾之湖边,听见旁边有两个妖精在对话,原来是Cirno和Daiyousei. Daiyousei:Cirno酱,我们趁着夜色,把整个幻想乡占领吧! Cirno:好啊!这样我们就可以到处玩了. 不知道你们反应如何,反正听到这段对话,Worldwide_D是笑出来了.幻想乡可以看作n个节点的树,每条道路都是有向边.Cirno可以消耗一点灵力,改变一条道路的方向. 如果最终得

【DTOJ】2702:余数

DTOJ 2702:余数  解题报告 2017.11.09 第一版 --由翱翔的逗比w原创 题目信息: 题目描述 输入两个整数,求他们相除的余数 输入 a b两个数 输出 a/b的余数 样例输入 3 2 样例输出 1 思路: 要求计算余数,则需要用到取模运算符(求余运算)"%",注意%左右两边的数据需为整型int 注意: a和b需声明为整型int变量 我的代码(C++): 1 //DTOJ 2702 2 #include <iostream> 3 using namespa

【DTOJ】2703:两个数的余数和商

DTOJ 2703:两个数的余数和商  解题报告 2017.11.10 第一版 --由翱翔的逗比w原创,引用<C++ Primer Plus(第6版)中文版> 题目信息: 题目描述 给你a和b,求他们的余数和非整数商.保留两位小数. 输出 余数和商 样例输入 5 3 样例输出 2 1.67 提示 1<=a,b<=10000 思路: 利用运算符计算出余数和商,取模运算在前一篇提到过不再多讲. 注意: 除法运算符(/)的行为取决于操作数的类型.如果两个操作数都是整数,则C++将执行整数

【DTOJ】2700:hello world

DTOJ 2700:hello world  解题报告 2017.11.06 第一版  --由翱翔的逗比w原创 题目信息: 题目描述 输出hello world 输出 hello world 样例输出 hello world 思路: 利用标准输出字符串"hello world". 注意: hello world单词需拼写正确,注意字符为英文小写半角 我的代码(C++): 1 #include <iostream> 2 using namespace std; 3 4 int

【DTOJ】1001:长方形周长和面积

DTOJ 1001:长方形周长和面积  解题报告 --由翱翔的逗比w原创 题目信息: 题目描述 已知长方形的长和宽,求长方形的周长和面积? 输入 一行:空格隔开的两个整数,表示长和宽 输出 一行:长方形的周长和面积,中间用空格隔开. 样例输入 3 4 样例输出 14 12 提示 周长为:2*(a+b),面积为:a*b 思路: 定义整型变量a,b并输入,定义整型变量周长c=2*(a+b)和面积s=a*b,输出c和s,注意c和s之间有空格. 我的代码(C++): 1 //DTOJ 1001 2 #i

【DTOJ】2704:数字互换

DTOJ 2704:数字互换  解题报告 2017.11.11 第一版 --由翱翔的逗比w原创 题目信息: 题目描述 输入两个数作为交换数,输出已交换顺序后的两个值. 输入 两个整数,空格隔开 输出 交换后的两个整数,空格隔开 样例输入 1 2 样例输出 2 1 思路: 我的方法是在输出时颠倒两数的位置,另一种方法是将a的值赋值给临时变量t,然后b赋值给a,t赋值给b 我的代码(C++): 1 //DTOJ 2704 2 #include <iostream> 3 using namespac

HDU 3316 My Brute(二维费用流)经典

My Brute Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 941    Accepted Submission(s): 372 Problem Description Seaco is a beautiful girl and likes play a game called "My Brute". Before Va

BZOJ 3316 JC loves Mkk 二分答案+单调队列

题目大意:给定一个环,要求在这个环上截取长度为偶数且在[L,R]区间内的一段,要求平均值最大 看到环果断倍增 看到平均值最大果断二分答案 看到长度[L,R]果断单调队列 对数组维护一个前缀和,对前缀和维护单调递增的单调队列 每扫过一个数sum[i],将sum[i-L]加入单调队列,再把距离i超过R的点删掉 长度为偶数?对奇数位置和偶数位置分别维护一个单调队列即可 每次找到大于0的子串之后记录一下分母再退出就行了 #include <cstdio> #include <cstring>

HDU 3316 My Brute(二维费用流)

My Brute Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 941    Accepted Submission(s): 372 Problem Description Seaco is a beautiful girl and likes play a game called "My Brute". Before Va