zoukankan      html  css  js  c++  java
  • 洛谷P4149 [IOI2011]Race

    dsu on tree

    题目链接

    点我跳转

    题目大意

    给出一棵树,每条边有权。求一条简单路径,使得路径和等于 (k),且边的数量最小。

    问最小数量是多少(若没有满足条件的则输出 -1​)

    解题思路

    定义 (dis_u) 表示节点 (u) 到根节点的距离,(dep_u) 表示节点 (u) 的深度

    那么树上任意两点 (u , v) 的简单路径和等于 (dis_u + dis_v - 2 × dis_{lca(u , v)})

    两点之间边的数量为 (dep_u + dep_v - 2 × dep_{lca(u , v)})

    那么问题就转换成在树上找到两点 $ u , v $ 使得 (dis_u + dis_v - 2 × dis_{lca(u , v)} = k)(dep_u + dep_v - 2 × dep_{lca(u , v)}) 尽可能小

    假设当前根节点为 (rt) ,那么对于子节点 (x) 的来说,能与它构成满足上述式子的 (y) 可能来自 (rt) 的其它分支,也可能 (y = rt)

    (相同分支内的节点答案的在根节点为 (rt) 的子节点的时候就已经算过了)

    于是我们可以定义 (mp_d) 表示当前距离(1)号点距离为 (d) 的节点的最小深度 , 定义(c = k+2 * dis_{rt} - dis{x})

    那么 (x) 提供的贡献就是 (mp_c + dep_x - 2 * dep_{rt})

    因为我们计算两点的简单路径权值和、两点简单路径的边的个数是根据它们的(lca),所以一个分支内的节点不能相互影响

    所以需要先对一个分支统计完贡献后,再添加它的信息

    (事实上 (rt) 的任意一个分支内的贡献在统计子节点为根的子树中就已经计算过了,上文也已经讲过了)

    AC_Code

    #include<bits/stdc++.h>
    #define rep(i,a,n) for (int i=a;i<=n;i++)
    #define int long long
    using namespace std;
    const int N = 3e5 + 10;
    struct Edge{
    	int nex , to , w;
    }edge[N << 1];
    int head[N] , TOT;
    void add_edge(int u , int v , int w)
    {
    	edge[++ TOT].nex = head[u] ;
    	edge[TOT].to = v;
    	edge[TOT].w = w;
    	head[u] = TOT;
    }
    map<int , int>mp;
    int n , k , ans = 0x3f3f3f3f;
    int hson[N] , HH , sz[N] , dep[N] , dis[N];
    void dfs(int u , int far)
    {
    	sz[u] = 1;
    	dep[u] = dep[far] + 1;
    	for(int i = head[u] ; i ; i = edge[i].nex)
    	{
    		int v = edge[i].to , w = edge[i].w;
    		if(v == far) continue ;
    		dis[v] = w + dis[u];
    		dfs(v , u);
    		sz[u] += sz[v];
    		if(sz[v] > sz[hson[u]]) hson[u] = v;
    	}
    }
    void change(int u , int far)
    {
    	if(mp.count(dis[u])) mp[dis[u]] = min(mp[dis[u]] , dep[u]);
    	else mp[dis[u]] = dep[u];
    	for(int i = head[u] ; i ; i = edge[i].nex)
    	{
    		int v = edge[i].to;
    		if(v == far || v == HH) continue ;
    		change(v , u);	
    	} 
    }
    void calc(int u , int far , int rt)
    {	
    	int x = k + 2 * dis[rt] - dis[u];
    	if(mp.count(x)) ans = min(ans , mp[x] + dep[u] - 2 * dep[rt]);
    	for(int i = head[u] ; i ; i = edge[i].nex)
    	{
    		int v = edge[i].to;
    		if(v == far || v == HH) continue ;
    		calc(v , u , rt);
    	}
    }
    void dsu(int u , int far , int op)
    {
    	for(int i = head[u] ; i ; i = edge[i].nex)
    	{
    		int v = edge[i].to;
    		if(v == far || v == hson[u]) continue ;
    		dsu(v , u , 0);
    	}
    	if(hson[u]) dsu(hson[u] , u , 1) , HH = hson[u];
    	int x = k + dis[u];
    	if(mp.count(x)) ans = min(ans , mp[x] + dep[u] - 2 * dep[u]); 
    	mp[dis[u]] = dep[u];
    	for(int i = head[u] ; i ; i = edge[i].nex)
    	{
    		int v = edge[i].to , w = edge[i].w;
    		if(v == far || v == HH) continue ;
    		calc(v , u , u) , change(v , u);	
    	}
    	HH = 0;
    	if(!op) mp.clear(); 
    }
    signed main()
    {
    	cin >> n >> k;
    	rep(i , 1 , n - 1)
    	{
    		int u , v , w;
    		cin >> u >> v >> w;
    		u ++ , v ++ ;
    		add_edge(u , v , w) , add_edge(v , u , w);
    	}
    	dfs(1 , 0);
    	dsu(1 , 0 , 0);
    	if(ans == 0x3f3f3f3f) cout << -1 << '
    ';
    	else cout << ans << '
    ';
    	return 0;
    }
    
  • 相关阅读:
    hdu 3268 09 宁波 现场 I
    hdu 3697 10 福州 现场 H
    CodeForces Round #521 (Div.3) D. Cutting Out
    #Leetcode# 226. Invert Binary Tree
    zufe 蓝桥选拔
    #Leetcode# 100. Same Tree
    #Leetcode# 6. ZigZag Conversion
    PAT 1084 外观数列
    #Leetcode# 38. Count and Say
    #Leetcode# 22. Generate Parentheses
  • 原文地址:https://www.cnblogs.com/StarRoadTang/p/14028300.html
Copyright © 2011-2022 走看看