zoukankan      html  css  js  c++  java
  • 【NOIP 2015】运输计划

    思路

    此题做法:二分答案 + LCA + 树上差分。

    我们很容易想到二分答案,毕竟答案满足单调性且较难直接计算。那么我们需要的就是一个 check 函数。我们的重点就在 check 函数上。

    我们现在已知我们二分出的答案 (x)。我们要判断是否可行。首先,我们先把所有运输计划的两点之间的距离大于 (x) 的找出来,时间复杂度 (O(m))。然后我们需要对这些找出来的边上经过的点标记一下。如果有 (num) 个询问的长度大于 (x),我们再找到被标记次数为 (num) 的所有边即可。为什么这样做?我们这是为了找出最优的虫洞,能让这些边减小量最大。因为这 (num) 条边的长度都超过了 (x)。所以我们一定要找出这 (num) 个询问都经过的边才行,找出最大值,和 (num) 条边中长度最大的比较一下即可。思路十分清晰,我们只需要用边差分的方式优化一下即可,复杂度为 (O(m))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+5;
    struct edge {
    	int to,nxt,w;
    } e[N<<1];
    int cnt,head[N];
    inline void add(int u,int v,int w) {
    	e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt,e[cnt].w=w;
    }
    int f[N][32],dep[N],dis[N];
    void build(int u,int fa) {
    	dep[u]=dep[fa]+1,f[u][0]=fa;
    	for(int i=1; (1<<i)<=dep[u]; i++) {
    		f[u][i]=f[f[u][i-1]][i-1];
    	}
    	for(int i=head[u]; i; i=e[i].nxt) {
    		int v=e[i].to;
    		if(v==fa) {
    			continue;
    		}
    		dis[v]=dis[u]+e[i].w,build(v,u);
    	}
    }
    int lca(int x,int y) {
    	if(dep[x]<dep[y]) {
    		swap(x,y);
    	}
    	for(int i=30; i>=0; i--) {
    		if(dep[f[x][i]]>=dep[y]) {
    			x=f[x][i];
    		}
    		if(x==y) {
    			return x;
    		}
    	}
    	for(int i=30; i>=0; i--) {
    		if(f[x][i]^f[y][i]) {
    			x=f[x][i],y=f[y][i];
    		}
    	}
    	return f[x][0];
    }
    int n,m,c[N],num,maxn;
    struct query {
    	int x,y,lca,d;
    } a[N];
    void dfs(int u,int fa,int val) {
    	for(int i=head[u]; i; i=e[i].nxt) {
    		int v=e[i].to,w=e[i].w;
    		if(v^fa) {
    			dfs(v,u,w),c[u]+=c[v];
    		}
    	}
    	if(c[u]==num) {
    		maxn=max(maxn,val);
    	}
    }
    bool check(int x) {
    	memset(c,0,sizeof(c)),maxn=num=0;
    	int need=0;
    	for(int i=1; i<=m; i++) {
    		if(a[i].d>x) {
    			need=max(need,a[i].d-x),c[a[i].x]++,c[a[i].y]++,c[a[i].lca]-=2,num++;
    		}
    	}
    	dfs(1,0,0);
    	return need<=maxn;
    }
    int main() {
    	scanf("%d %d",&n,&m);
    	for(int i=1,u,v,w; i<n; i++) {
    		scanf("%d %d %d",&u,&v,&w),add(u,v,w),add(v,u,w);
    	}
    	build(1,0);
    	for(int i=1; i<=m; i++) {
    		scanf("%d %d",&a[i].x,&a[i].y);
    		a[i].lca=lca(a[i].x,a[i].y);
    		a[i].d=dis[a[i].x]+dis[a[i].y]-2*dis[a[i].lca];
    	}
    	int l=0,r=1<<30;
    	while(l<r) {
    		int mid=(l+r)>>1;
    		if(check(mid)) {
    			r=mid;
    		} else {
    			l=mid+1;
    		}
    	}
    	printf("%d",l);
    	return 0;
    }
    

    参考资料

    [NOIP-2015] 运输计划 - huayucaiji

    本文作者:AFewMoon,文章地址:https://www.cnblogs.com/AFewMoon/p/15411696.html

    知识共享许可协议

    本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

    限于本人水平,如果文章有表述不当之处,还请不吝赐教。

  • 相关阅读:
    基贝叶斯后验概率的拼写检查器实现 python
    Hadoop The Definitive Guide 2nd Edition 笔记
    DGV删除当前选中行
    DGV设置单元不可编辑
    DGV获取当前选中单元格
    HDU2112_HDU Today_有地名的最短路_map+SPFA
    HDU1026_优先队列+广搜+打印路径
    HDU2757_Ocean Currents_优先队列+广搜_入门题_十四周模拟赛
    自己写的SPFA模板(可打印路径)
    HDU2782_暴力深搜_第十四周模拟赛
  • 原文地址:https://www.cnblogs.com/Sam2007/p/15411696.html
Copyright © 2011-2022 走看看