zoukankan      html  css  js  c++  java
  • [NOI2003]逃学的小孩 题解

    前言
    >原题传送门(洛谷)<
    看了一下洛谷题面,这道NOI的题竟然是蓝的(恶评?),做了一下好像确实是蓝的...
    解法
    思路非常简单,找道树的直径,然后答案是直径长度加上最大的min(dis[pos1], dis[pos2]),pos1和pos2是指定的任意一条直径的两个端点,dis是距离
    证明
    鉴于这是一棵树(原题面:可以保证,任两个居住点间有且仅有一条通路。)
    因此,我们最大的方案必然包含一条直径
    可以稍加思考,如果不是直径的话一定能找到一种取直径的方法比它更大...
    那么再任意找另一个点,因为要满足"如果A距离C比B距离C近走A,否则走B",所以任意一个点的贡献为min(dis[pos1], dis[pos2])。
    题目要求最大的,所以取最大的min(dis[pos1], dis[pos2])
    代码
    树的直径显然只需要一个DFS,求解也只需要一个DFS,所以共计两个DFS

    #include <cstdio>
    #include <algorithm>
    #define ll long long 
    
    using namespace std;
    
    ll read(){
        ll x = 0; int zf = 1; char ch = ' ';
        while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    
    struct Edge{
    	int to, next;
    	ll dis;
    } edges[400005];
    
    int head[200005], edge_num;
    
    void addEdge(int u, int v, ll w){
    	edges[++edge_num] = {v, head[u], w};
    	head[u] = edge_num;
    }
    
    ll dis[200005];
    ll dis2[200005][2];
    
    void getDis(int u, int fa, ll vl){
    	int v; dis[u] = ((u == fa) ? 0 : dis[fa] + vl);
    	for (int c_e = head[u]; c_e; c_e = edges[c_e].next){
    		v = edges[c_e].to; if (v == fa) continue;
    		getDis(v, u, edges[c_e].dis);
    	}
    }
    
    void getDis2(int u, int fa, ll vl, int op){
    	int v; dis2[u][op] = ((u == fa) ? 0 : dis2[fa][op] + vl);
    	for (int c_e = head[u]; c_e; c_e = edges[c_e].next){
    		v = edges[c_e].to; if (v == fa) continue;
    		getDis2(v, u, edges[c_e].dis, op);
    	}
    }
    
    int main(){
    	int n = read(), m = read();
    	for (int i = 1; i <= m; ++i){
    		int u = read(), v = read(); ll w = read();
    		addEdge(u, v, w), addEdge(v, u, w);
    	}
    	getDis(1, 1, 0);
    	int pos1; ll _max = -1;
    	for (int i = 1; i <= n; ++i)
    		if (dis[i] > _max)
    			_max = dis[i], pos1 = i;
    	getDis(pos1, pos1, 0);
    	int pos2; _max = -1;
    	for (int i = 1; i <= n; ++i)
    		if (dis[i] > _max)
    			_max = dis[i], pos2 = i;
    	getDis2(pos1, pos1, 0, 0);
    	getDis2(pos2, pos2, 0, 1);
    	ll _max2 = -1;
    	for (int i = 1; i <= n; ++i)
    		_max2 = max(_max2, ((dis2[i][0] > dis2[i][1]) ? dis2[i][1] : dis2[i][0]));
    	printf("%lld", _max + _max2);
    	return 0;
    }
    

    备注
    求树的直径:从任意一点开始DFS,找到最远点pos1,再从pos1开始DFS找到最远点pos2,
    pos1和pos2即为树的直径
    证明略(易证)

  • 相关阅读:
    [转] SQL Server中的行列转换问题
    【原】Sql中时间处理函数 DateAdd & DateDiff
    [转] 为GridView删除添加确认对话框
    【转】jQuery入门指南教程
    [原] Excel(VBA)中数据的非科学记数法显示
    【原】IP地址存储问题
    【转】 asp.net数据导出EXCEL
    【原】有线通设定
    【原】 VS2005/VWD2005调试错误“无法附加,绑定句柄无效”的解决
    [原] Excel中计算2个日期间的时间间隔
  • 原文地址:https://www.cnblogs.com/linzhengmin/p/10991898.html
Copyright © 2011-2022 走看看