BZOJ 3246 IOI 2013 Dreaming 树形DP

题目大意

给出一个缺若干条边的树,现在让你填一些长度为定值的边,使得整个树的直径最小。

思路

给一个详细的网址,讲的非常明白。

http://www.ccf.org.cn/resources/1190201776262/fujian/xuhaoran2013-07-25-03_33_55.pdf

还有数据范围是50w。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 500010
using namespace std;

int points,edges,cost;
int head[MAX],total;
int _next[MAX << 1],aim[MAX << 1],length[MAX << 1];
int father[MAX],dis[MAX];
int f[MAX];

int Find(int x)
{
    if(f[x] == x)   return x;
    return f[x] = Find(f[x]);
}

inline void Add(int x,int y,int len)
{
    _next[++total] = head[x];
    aim[total] = y;
    length[total] = len;
    head[x] = total;
}

int max_length[MAX],next_length[MAX];
int max_son[MAX];

void DFS(int x,int last)
{
    father[x] = last;
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last)  continue;
        DFS(aim[i],x);
        if(max_length[aim[i]] + length[i] > max_length[x]) {
            max_length[x] = max_length[aim[i]] + length[i];
            max_son[x] = aim[i];
        }
    }
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] != last && aim[i] != max_son[x])
            next_length[x] = max(next_length[x],max_length[aim[i]] + length[i]);
    }
}

int g[MAX];
int cnt,now_tree;

int __max[MAX],__min[MAX];

void Calc(int x,int last)
{
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last)  continue;
        g[aim[i]] = max(g[x],max_son[x] == aim[i] ? next_length[x]:max_length[x]) + length[i];
        int now = max(g[aim[i]],max_length[aim[i]]);
        __max[now_tree] = max(__max[now_tree],now);
        __min[now_tree] = min(__min[now_tree],now);
        Calc(aim[i],x);
    }
}

int main()
{
    cin >> points >> edges >> cost;
    for(int i = 1; i <= points; ++i)
        f[i] = i;
    for(int x,y,z,i = 1; i <= edges; ++i) {
        scanf("%d%d%d",&x,&y,&z);
        ++x,++y;
        Add(x,y,z),Add(y,x,z);
        int fx = Find(x),fy = Find(y);
        if(fx != fy)
            f[fx] = fy;
    }
    int ans = 0;
    int max_1 = 0,max_2 = 0,max_3 = 0;
    for(int i = 1; i <= points; ++i)
        if(f[i] == i) {
            ++cnt;
            now_tree = i;
            DFS(i,0);
            g[i] = 0;
            __max[i] = __min[i] = max_length[i];
            Calc(i,0);
            ans = max(ans,__max[i]);
            if(__min[i] > max_1) {
                max_3 = max_2;
                max_2 = max_1;
                max_1 = __min[i];
            }
            else if(__min[i] > max_2) {
                max_3 = max_2;
                max_2 = __min[i];
            }
            else
                max_3 = max(max_3,__min[i]);
        }
    if(cnt > 1) ans = max(ans,max_1 + max_2 + cost);
    if(cnt > 2) ans = max(ans,max_2 + max_3 + (cost << 1));
    cout << ans << endl;
    return 0;
}
时间: 2024-10-28 05:40:58

BZOJ 3246 IOI 2013 Dreaming 树形DP的相关文章

[BZOJ 4033] [HAOI2015] T1 【树形DP】

题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Father[i] 之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值). 然后DFS来求,枚举 i 的每个儿子 j,现在的 f[i][] 是包含了 [1, j-1] 子树,然后两重循环枚举范围是 [1, j - 1] 的子树总 Size 和

bzoj 3566: [SHOI2014]概率充电器 树形DP

首先普及一个概率公式 P(A+B)=P(A)+P(B)-P(AB) 题意:一些充电元件和导线构成一棵树,充电元件是否能充电有2种情况, 1.它自己有qi%的概率充电 2.与它相邻的元件通过导线给它充电(导线有p%的概率导通) 求最终充了电的元件的期望 题解:首先可以将元件能否充电分成3种情况考虑, 1.它自己给自己充好了电 2.它的儿子方向给它传送了电 3.它的父亲方向给它传送了电. 对于1,题目已经给出可以直接赋值, 对于2,可以通过一次树的深度遍历求得.pson[now]=pson[now]

BZOJ 2878([Noi2012]迷失游乐园-树形DP+环加外向树+期望DP+vector的erase)

2878: [Noi2012]迷失游乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved: 223 [Submit][Status] Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1).小Z现在所在的大门也正好是

BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )

一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm&

BZOJ 2435 道路修建 NOI2011 树形DP

一看到这道题觉得很水,打了递归树形DP后RE了一组,后来发现必须非递归(BFS) 递归版本84分: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int N,point[1000003],next[2000003],v[2000003],c[2000003],cnt=0,f[1000003]; bool p[1000003]; long long sum=0; vo

BZOJ 2314: 士兵的放置( 树形dp )

树形dp... dp(x, 0)表示结点x不放士兵, 由父亲控制: dp(x, 1)表示结点x不放士兵, 由儿子控制: dp(x, 2)表示结点x放士兵. ------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace st

BZOJ 2286 消耗战 (虚树+树形DP)

给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<=500000) 考虑树形DP,我们令mn[i]表示i节点无法与1节点相连切除的最小权值.显然有mn[i]=min(E(fa,i),mn[fa]).大致就是i到1的简单路径上的最小边.我们对于每个询问.把询问的点不妨称为关键点.令dp[i]表示i节点不能与子树的关键点连接切掉的最小权值.那么有,如果son[i]

BZOJ 2500 幸福的道路 树形DP+单调队列

题目大意:给定一棵树,令a[i]为从第i个节点出发的最长链,求a[i]中最长的区间,满足区间内最大值与最小值之差不超过m 读错题害死人,脑残害死人 求a[i]显然是树形DP 考虑从一个点出发的链可以从子节点走,也可以从父节点走 因此我们DP两次,第一次求出从子节点走的最长链,第二次求出从父节点走的最长链,两次取max就是答案 但是直接DP会有问题,因为从父节点走的最长链可能是从自己的子树出发的,这样就会走重 因此除记录从子节点出发的最长链外还要记录一个从另一个子节点出发的次长链,如果最长链长度相

BZOJ 4726 POI 2017 Sabota? 树形DP

4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved: 49[Submit][Status][Discuss] Description 某个公司有n个人, 上下级关系构成了一个有根树.其中有个人是叛徒(这个人不知道是谁).对于一个人, 如果他下属(直接或者间接, 不包括他自己)中叛徒占的比例超过x,那么这个人也会变成叛徒,并且他的所有下属都会变成叛徒