• 普通图论题


    【题目描述】
    给定一棵(n)个点的树,标记出(m)个不同的点(s_1,s_2,s_3,cdots,s_m),对于每个点(s_i)求出剩下的标记点中哪个离(s_i)最近,输出距离。

    【输入格式】
    第一行一个整数(n)
    (2sim n)行每行两个整数(u,w),第(i)行表示(i)与父亲(u)之间有一条权为(w)的边。

    接下来一行一个整数(m)
    接下来一行(m)个整数表示(s_1sim s_m)

    【输出格式】
    输出一行空格隔开的(m)个整数,第(i)个数代表(s_i)的答案。

    【数据范围】
    (2 le m le n le 10^6)(0 le w le 10^9)

    暴力做法就很简单 什么最短路LCA之类的随便搞一搞就行
    然而还是要DP

    假设根节点是1,令(f[i])表示(i)子树内的最小答案,这个答案显然是一遍dfs就能跑出来。
    子树外的答案怎么推呢?

    其实也挺简单 设(ans[i])(i)的最终答案 设(fa)(i)的父亲
    情况1 (ans[fa])指的就是(fa)(i)的距离 即距离(fa)最近的标记点就是(i)
    此时用(fa)次小值(ans2[fa])来更新(ans[i])(ans[i]=min(ans[i], ans2[fa]+dis[fa][i]) quad dis[fa][i])(i)(fa)边的长度

    情况2 距离(fa)最近的标记点不是(i)(或者(i)根本不是标记点)
    显然(ans[i]=min(ans[i], ans[fa]+dis[fa][i]))

    在更新最小值的同时顺便更新一下次小值就行了 具体看代码

    【代码】

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define re register
    using namespace std;
    typedef long long ll;
    
    ll read() {
    	ll ret = 0, flag = 1;
    	char ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') flag = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		ret = (ret << 1) + (ret << 3) + (ch ^ '0');
    		ch = getchar();
    	}
    	return ret * flag;
    }
    
    ll n, m, head[2000005], pre[2000005], to[2000005], val[2000005], sz, s[1000005];
    bool spe[1000005];
    ll f[1000005], ind[1000005], f2[1000005], ind2[1000005];
    
    inline void insert(ll u, ll v, ll w) {
    	to[++sz] = v; pre[sz] = head[u]; head[u] = sz; val[sz] = w;
    } 
    
    void update(ll x, ll y, ll v) {
    	if (v < f[x]) {
    		f2[x] = f[x]; ind2[x] = ind[x];
    		f[x] = v; ind[x] = y; 
    	} else if (v < f2[x]) {
    		f2[x] = v; ind2[x] = y;
    	}
    }
    
    void dfs(ll x, ll fa) {
    	if (spe[x]) {
    		f[x] = 0;
    		ind[x] = x;
    	}
    	for (re int i = head[x]; i; i = pre[i]) {
    		ll y = to[i];
    		if (y == fa) continue;
    		dfs(y, x);
    		update(x, y, f[y] + val[i]);
    	}
    }
    
    void dfs2(ll x, ll fa) {
    	for (re int i = head[x]; i; i = pre[i]) {
    		ll y = to[i];
    		if (y == fa) continue;
    		if (y == ind[x]) {
    			if (f[y] > f2[x] + val[i]) {
    				f2[y] = f[y]; ind2[y] = ind[y];
    				f[y] = f2[x] + val[i]; ind[y] = ind2[x];
    			} else if (f2[y] > f2[x] + val[i]) {
    				f2[y] = f2[x] + val[i]; ind2[y] = ind2[x];
    			}
    		} else {
    			if (f[y] > f[x] + val[i]) {
    				f2[y] = f[y]; ind2[y] = ind[y];
    				f[y] = f[x] + val[i]; ind[y] = ind[x];
    			} else if (f2[y] > f[x] + val[i]) {
    				f2[y] = f[x] + val[i]; ind2[y] = ind[x];
    			}
    		}
    		dfs2(y, x);
    	}
    }
    
    int main() {
    	n = read();
    	for (re int i = 2; i <= n; i++) {
    		ll u, w;
    		u = read(); w = read();
    		insert(i, u, w); insert(u, i, w);
    	}
    	m = read();
    	for (re int i = 1; i <= m; i++) {
    		s[i] = read();
    		spe[s[i]] = 1;
    	}
    	for (re int i = 1; i <= n; i++) {
    		f[i] = f2[i] = 0x7ffffffffffffff;
    	}
    	dfs(1, 0);
    	dfs2(1, 0);
    	for (re int i = 1; i <= m; i++) {
    		printf("%lld ", f2[s[i]]);
    	}
    	puts("");
    	return 0;
    }
    

    ps: 这题(10^6)蜃臭还是要卡卡常才能过
    三年OI一场空 不开long long 见祖宗

  • 相关阅读:
    Protected和Default的区别
    将数组中负数放在正数前面
    java.io包和杯子测楼
    hadoop基础
    极限编程和JUnit
    接口和抽象类
    C# 中窗口AutoScaleMode属性
    计算机的自启动管理
    labview中的移位寄存器、循环隧道,自动索引隧道的区别
    发现C#winform编程中不常用的控件(一)<FlowLayoutPanel控件><拆分器控件Splitcontainer >
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM23.html
Copyright © 2020-2023  润新知