zoukankan      html  css  js  c++  java
  • 运输计划

    题目就是询问将树上哪一条边的权重设为0之后,给定的m条路径的长度的最大值的最小。

    我们先考虑序列上怎样做,再扩展到树上。我们先将边权转化为点权,对于最大值最小的问题,考虑二分答案。问题就转化为:是否存在一个数,权值为0之后,所有给定路径的长度和都小于等于mid(其实也就是最大值 (leq) mid)。对于一条路径,如果它的长度 > mid,那么一定要删除这条路径上的一条边,我们应该将这样的边设为0,即所有长度大于mid的路径的交集。差分就可以帮我们找出这样的边,可以用差分进行区间加的标记,看一看一条边的覆盖次数即可。

    45分代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define x first
    #define y second
    using namespace std;
    
    const int N = 3e5 + 100;
    
    typedef pair<int, int> PII;
    struct Edge {
    	int v, w, nxt;
    } e[ N << 1];
    int n, m, cnt, head[N], seq[N], dis[N];
    int sum[N], b[N];
    PII p[N];
    
    void AddEdge(int u, int v, int w) {
    	e[++cnt].v = v;
    	e[cnt].w = w;
    	e[cnt].nxt = head[u];
    	head[u] = cnt;
    }
    
    bool check(int mid) {
    	int nums = 0, maxw = 0;
    	for(int i = 1; i <= n; i ++)    b[i] = 0; 
    	for(int i = 1; i <= m; i++) {
    		int u = p[i].x, v = p[i].y;
    		if( u > v)	swap(u, v);
    		int d = sum[v - 1] - sum[u - 1];
    		if(d > mid) {
    			b[u] += 1, b[v] -= 1;
    			nums ++;
    			maxw = max(d, maxw);
    		}
    	}
    	for(int i = 1; i <= n; i ++)
    		b[i] = b[i] + b[i - 1];
    	for(int i = 1; i < n; i ++) {
    		if( b[i] == nums) {
    			if( maxw - seq[i] <= mid)
    				return true;
    		}
    	}
    	return false;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i < n; i ++) {
    		int u, v, w;
    		scanf("%d%d%d", &u, &v, &w);
    		AddEdge(u, v, w), AddEdge(v, u, w);
    		seq[i] = w;
    	}
    	for(int i = 1; i <= m; i ++) {
    		scanf("%d%d", &p[i].x, &p[i].y);
    	} 
    	for(int i = 1; i <= n; i ++)
    		sum[i] = sum[i - 1] + seq[i];
    	int l = 0, r = 1e9;
    	while(l < r) {
    		int mid = (l + r) >> 1;
    		if( check(mid))
    			r = mid;
    		else l = mid + 1;
    	}
    	printf("%d
    ", l);
    	return 0;
    }
    

    对于树上的问题,我们只需要将前缀和转化为树上前缀和,差分转化为树上差分即可。

    /*
    统计将一条边的边权设为 0 之后的最大路径长度 
    max( dist(u, v))
    
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define x first
    #define y second
    
    using namespace std;
    
    const int N = 3e5 + 100;
    typedef pair<int, int> PII;
    
    struct Edge {
    	int v, w, nxt;
    } e[ N << 1];
    
    int n, m, cnt, head[N], dis[N], LCA[N], nums, maxw;
    int dep[N], anc[N][22];
    int sum[N], b[N];
    PII p[N];
    bool flag = false;
    
    void dfs_lca(int u, int ff) {
    	anc[u][0] = ff;
    	dep[u] = dep[ff] + 1;
    	for(int i = 1; i <= 20; i ++) 
    		anc[u][i] = anc[anc[u][i - 1]][i - 1];
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].v;
    		if( ff == v)	continue;
    		dfs_lca(v, u);
    	} 
    }
    
    
    int lca(int u, int v) {
    	if( dep[u] < dep[v]) swap(u, v);
    	for(int i = 20; i >= 0; -- i) {
    		if( dep[anc[u][i]] >= dep[v])
    			u = anc[u][i];
    	}
    	if( u == v) return u;
    	for(int i = 20; i >= 0; -- i) {
    		if( anc[u][i] != anc[v][i])
    			u = anc[u][i], v = anc[v][i];
    	}
    	return anc[u][0];
    }
    
    void AddEdge(int u, int v, int w) {
    	e[++cnt].v = v;
    	e[cnt].w = w;
    	e[cnt].nxt = head[u];
    	head[u] = cnt;
    }
    
    void dfs_df(int u, int fa) {
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].v;
    		if( v == fa)	continue;
    		dfs_df(v, u);
    		b[u] += b[v];
    	}
    }
    
    void dfs_ans(int u, int ff, int mid) {
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].v;
    		if( v == ff)	continue;
    		if(b[v] == nums)
    			if( maxw - e[i].w <= mid)
    				flag = true;
    		dfs_ans(v, u, mid);
    	}
    }
    
    bool check(int mid) {
    	nums = 0, maxw = 0;
    	for(int i = 1; i <= n; i ++)    b[i] = 0;
    	for(int i = 1; i <= m; i++) {
    		int u = p[i].x, v = p[i].y;
    		if(dis[i] > mid) {
    			b[u] += 1, b[v] += 1;
    			b[LCA[i]] -= 2;
    			nums ++;
    			maxw = max(dis[i], maxw);
    		}
    	}
    	dfs_df(1, 0);
    	//进行一遍dfs,通过每条边的覆盖次数来统计答案
    	flag = false;
    	dfs_ans(1, 0, mid); 
    	return flag;
    }
    
    void dfs_sum(int u, int fa) {
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].v;
    		if( v == fa)	continue;
    		sum[v] = sum[u] + e[i].w;//错误1,回溯之前算 
    		dfs_sum(v, u);
    	}
    }
    
    void pre_dis() {
    	for(int i = 1; i <= m; i ++) {
    		int u = p[i].x, v = p[i].y;
    		LCA[i] = lca(u, v);
    		dis[i] = sum[u] + sum[v] - 2 * sum[LCA[i]];
    	}
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i < n; i ++) {
    		int u, v, w;
    		scanf("%d%d%d", &u, &v, &w);
    		AddEdge(u, v, w), AddEdge(v, u, w);
      	}
    	for(int i = 1; i <= m; i ++) {
    		scanf("%d%d", &p[i].x, &p[i].y);
    	}
    	dfs_lca(1, 0);//错误2:没有预处理 
    	dfs_sum(1, 0);//树上前缀和 
    	pre_dis();//树上两点间距离 
    	int l = 0, r = 2e9;
    	while(l < r) {
    		int mid = (l + r) >> 1;
    		if( check(mid))
    			r = mid;
    		else l = mid + 1;
    	}
    	printf("%d
    ", l);
    	return 0;
    }
    
    
  • 相关阅读:
    roportional Rate Reduction (PRR)
    【C++11新特性】 nullptr关键字
    C++ 智能指针
    std::thread
    C++11 的 std::ref 用法
    for auto
    C++11右值引用与移动构造函数
    leetcode刷题笔记一百零六题 从中序与后序遍历序列构造二叉树
    leetcode刷题笔记一百零五题 从前序与中序遍历序列构造二叉树
    leetcode刷题笔记一百零四题 二叉树的最大深度
  • 原文地址:https://www.cnblogs.com/wyy0804/p/13773392.html
Copyright © 2011-2022 走看看