zoukankan      html  css  js  c++  java
  • 【YBTOJ】【Luogu P2680】[NOIP2015 提高组] 运输计划

    【YBTOJ】【Luogu P2680】[NOIP2015 提高组] 运输计划:

    链接:

    洛谷

    题目大意:

    在一棵 (n) 个节点的树中,给定 (m) 条路径,现在能把树中某边边权改为零,求最大路径边权和最小值。

    正文:

    “求最大路径边权和最小值”可以联系到二分答案,二分最大路径边权和。在二分的过程中,一些路径边权和要大于二分值,我们叫它们非法路径,就要在这些路径中找出一条它们共同覆盖的边,且边权最大。那么可以通过确定最大的非法路径减去我们要找的最大的边与二分值的大小关系确定二分范围。

    接下来的目标就是找边。根据边的条件,它需要被覆盖于所有非法路径,那么通过树上差分记录每条边被经过的次数。在被经过次数等于非法路径数的边中,找个边权最大的就是了。

    代码:

    const int N = 300010;
    
    inline ll Read()
    {
    	ll x = 0, f = 1;
    	char c = getchar();
    	while (c != '-' && (c < '0' || c > '9')) c = getchar();
    	if (c == '-') f = -f, c = getchar();
    	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
    	return x * f;
    }
    
    // main
    int n, m, logN;
    ll ans;
    struct Question
    {
    	ll x, y, lca, dis;
    	bool operator < (Question &a) const
    	{
    		return dis > a.dis;
    	}
    }Q[N];
    
    // edge
    struct edge
    {
    	int from, to, val, nxt;
    }e[N << 1];
    int head[N], tot;
    
    void add(int u, int v, int w)
    {
    	e[++tot] = (edge) {u, v, w, head[u]}, head[u] = tot;
    	e[++tot] = (edge) {v, u, w, head[v]}, head[v] = tot;
    }
    
    // LCA
    ll dis[N];
    bool vis[N];
    int dep[N], f[N][30], fa[N];
    queue <int> q;
    
    void bfs(int root)
    {
    	for(; !q.empty(); q.pop());
    	q.push(root);
    	dep[root] = 1;
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		vis[u] = 1;
    		for (int v, i = head[u]; i; i = e[i].nxt)
    		{
    			if (vis[v = e[i].to]) continue;
    			fa[v] = u;
    			dep[v] = dep[u] + 1;
    			dis[v] = dis[u] + e[i].val;
    			f[v][0] = u;
    			for (int j = 1; j <= logN; j++)
    				f[v][j] = f[f[v][j - 1]][j - 1];
    			q.push(v);
    		} 
    	}
    }
    
    int LCA(int u, int v)
    {
    	if (dep[u] > dep[v]) u ^= v ^= u ^= v;
    	for (int j = logN; ~j; j--)
    		if (dep[f[v][j]] >= dep[u]) 
    			v = f[v][j];
    	if(u == v) return u;
    	for (int j = logN; ~j; j--)
    		if (f[v][j] != f[u][j]) 
    			u = f[u][j], v = f[v][j];
    	return f[u][0];
    }
    
    // binary - check
    int val[N];
    void dfs(int u, int fa)
    {
    	for (int v, i = head[u]; i; i = e[i].nxt)
    	{
    		if ((v = e[i].to) == fa) continue;
    		dfs(v, u);
    		val[u] += val[v];
    	}
    }
    
    bool check(ll x)
    {
    	memset (val, 0, sizeof val);
    	int num = 0;
    	for (int i = 1; i <= m; i++)
    	{
    		if (Q[i].dis <= x) break;
    		val[Q[i].x] ++, val[Q[i].lca] --;
    		val[Q[i].y] ++, val[Q[i].lca] --;
    		num++;
    	}
    	dfs(1, 0);
    	ll Max = 0;
    	for (int i = 1; i <= n; i++)
    		if(val[i] == num) Max = max(Max, dis[i] - dis[fa[i]]);
    	return Q[1].dis - Max > x;
    }
    
    ll l, r, mid;
    // main()-
    int main()
    {
    	n = Read(), m = Read(); logN = log2(n) + 1;
    	for (ll u, v, w, i = 1; i < n; i++)
    		u = Read(), v = Read(), w = Read(), 
    		add(u, v, w), l = max(l, w);
    	bfs(1);
    	for (int i = 1, u, v, c; i <= m; i++)
    		u = Read(), v = Read(),
    		Q[i] = (Question){u, v, c = LCA(u, v), dis[u] + dis[v] - 2 * dis[c]},
    		r = max(r, Q[i].dis);
    	sort (Q + 1, Q + 1 + m);
    	l = r - l;
    	while (l <= r) 
    	{
    		mid = l + r >> 1;
    		if (check(mid)) l = mid + 1;
    		else ans = mid, r = mid - 1;
    	}
    	
    	printf ("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    day09 文件操作
    深信服二面
    test1
    视频测试
    通过独立按键控制LED灯
    第一个LED灯
    为什么我的递归调用次数和书上的不一样?
    函数指针数组
    虚拟内存
    单元测试
  • 原文地址:https://www.cnblogs.com/GJY-JURUO/p/14983601.html
Copyright © 2011-2022 走看看