zoukankan      html  css  js  c++  java
  • 题解 P2680 【运输计划】

    题目链接

    不得不说,这真是一道优(s)质(b)图()论()题.相信它能给拼搏于逐梦之路上的你有力的援助,让你感受到出题人的满满善意(大雾)

    Solution [NOIP2015]运输计划

    题目大意:给定一棵树,以及(m)条简单路径,你可以使任意一条边的权值变为(0),询问最大路径长度的最小值

    二分答案,树上差分


    分析:假设出题人良心发现不卡常,我们看看怎么做

    "最大值最小"我们可以考虑二分

    首先我们把路径长度求出来(这玩意儿可以树上差分 / 倍增),然后对其按升序排序

    差分做法:设有两点(u,v),(LCA)(l),(dis[u])(u)到根节点路径长度,则路径((u,v))长为(dis[u] + dis[v] - 2 imes dis[l])

    假设我们二分一个答案(limit),我们可以很快的把所有长度(dis > limit)的路径找出来,假设这样的路径有(num)

    然后我们找被(num)个路径共同经过的边,如果没有,那么可以判(limit)这个答案为(false)(因为无论你怎么改总有路径长度(>limit))

    然后假如有多条这样边,贪心选取其中权值最大的,看一下长度最大的路径减去这个边权后长度是否(leq limit)即可

    于是问题变成了把一段路径上所有边权增加(1),询问一条边的边权,我们可以考虑把边权下放到深度较深的点,然后继续树上差分

    假如要修改(u,v)的话,我们把(sum[u]+1,sum[v]+1,sum[l]-2),然后子树和就是点权(即为边权)

    下面就是喜闻乐见的卡常时间

    • (1.)发现求(LCA)次数较多,而且可以离线做,考虑用(Tarjan)算法优化
    • (2.)读入优化,配合(fread)食用更佳
    • (3.)子树求和,求出树的(dfs)序,然后倒着用(dp)刷表法思想刷新父亲,因为儿子一定比父亲晚出现,这个优化让我最大数据点从(1400ms+)(327ms)……
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn = 3e5 + 100;
    namespace FastIO{
        typedef int type;
        const int bufsize = 1 << 20;
        char buf[bufsize],*s,*t;
        inline char gc(){
            if(s == t)s = buf,t = buf + fread(buf,sizeof(char),bufsize,stdin);
            return *(s++);
        }
        inline type read(){
            type ret = 0;char c = gc();
            while(c < '0' || c > '9')c = gc();
            while(c >= '0' && c <= '9'){
                ret = ret * 10 + c - '0';
                c = gc();
            }
            return ret;
        }
    }using FastIO::read;
    struct Ques{int v,id;}ques[maxn << 1];
    struct Edge{int to,dist;}Edges[maxn << 1];
    int head[maxn],nxt[maxn << 1],headq[maxn],nxtq[maxn << 1];
    inline void addques(int u,int v,int id){
    	static int tot;
    	ques[++tot] = Ques{v,id};
    	nxtq[tot] = headq[u];
    	headq[u] = tot;
    }
    inline void addedge(int from,int to,int dist){
    	static int tot;
    	Edges[++tot] = Edge{to,dist};
    	nxt[tot] = head[from];
    	head[from] = tot;
    }
    
    struct Road{int u,v,lca,val;}road[maxn];
    int f[maxn],dfn[maxn],rnk[maxn],faz[maxn],sum[maxn],val[maxn],n,m,dis[maxn],l,r,ans,dfs_tot;
    inline int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}
    inline void dfs_init(int u = 1){
    	f[u] = u;
    	dfn[u] = ++dfs_tot;
    	rnk[dfn[u]] = u;
    	for(int i = head[u];i;i = nxt[i]){
    		const Edge &e = Edges[i];
    		if(dfn[e.to])continue;
    		dis[e.to] = dis[u] + e.dist;
    		val[e.to] = e.dist;
    		faz[e.to] = u;
    		dfs_init(e.to);
    		f[e.to] = u;
    	}
    	for(int i = headq[u];i;i = nxtq[i]){
    		const Ques &q = ques[i];
    		if(dfn[q.v])road[q.id].lca = find(q.v);
    	}
    }
    inline int query(int u,int v,int l){return dis[u] + dis[v] - 2 * dis[l];}
    inline void modify(int u,int v,int l){sum[u]++,sum[v]++,sum[l] -= 2;}
    inline int max(int a,int b){return a > b ? a : b;}
    inline bool check(int limit){
    	if(road[m].val <= limit)return true;
    	memset(sum,0,sizeof(sum));
    	int now = 0,mx = -1;
    	for(int i = m;i >= 1 && road[i].val > limit;i--,now++)
    		modify(road[i].u,road[i].v,road[i].lca);
    	for(int i = n;i >= 1;i--)
    		sum[faz[rnk[i]]] += sum[rnk[i]];
    	for(int i = 1;i <= n && road[m].val - mx > limit;i++)
    		if(sum[i] == now)mx = max(mx,val[i]);
    	return road[m].val - mx <= limit;
    }
    int main(){
    	n = read(),m = read();
    	for(int u,v,w,i = 1;i < n;i++)
    		u = read(),v = read(),w = read(),addedge(u,v,w),addedge(v,u,w);
    	for(int i = 1;i <= m;i++)
    		road[i].u = read(),road[i].v = read(),addques(road[i].u,road[i].v,i),addques(road[i].v,road[i].u,i);
    	dfs_init();
    	for(int i = 1;i <= m;i++)
    		road[i].val = query(road[i].u,road[i].v,road[i].lca),r = max(r,road[i].val);
    	sort(road + 1,road + 1 + m,[](const Road &a,const Road &b){return a.val < b.val;});
    	int tot = 0;
    	while(l <= r){
    		int mid = (l + r) >> 1;
    		tot++;
    		if(check(mid))ans = mid,r = mid - 1;
    		else l = mid + 1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    (转载) 随机数原理
    ZOJ 2588 Burning Bridges(求桥的数量,邻接表)
    生成不重复的随机数对(C/C++)
    比较两个文件是否相同(C/C++语言)
    计算文件大小(C/C++语言)
    (转载)Nim游戏博弈(收集完全版)
    将一串字符串全排列输出(回溯法)
    Linux中使用Crontab定时监测维护Tomcat应用程序的方法
    Nginx单向认证的安装配置
    非关系型数据库 2017-02-12 22:27 189人阅读 评论(2) 收藏
  • 原文地址:https://www.cnblogs.com/colazcy/p/11675817.html
Copyright © 2011-2022 走看看