• 洛谷 P2680 [NOIP2015 提高组] 运输计划(二分,树上查分,树上倍增,LCA)


    传送门


    比较综合的一道题。

    解题思路

    求把一条边变为0后这m条路径中的最短值,最大值最小,可以二分求解。

    如何check某个答案x是否合法?
    实质就是判断能否找到一条边,使得大于x的路径都经过这条边,并且减去这条边边权后路径长度都小于等于x。

    于是我们先预处理出要求的路径的原长度(倍增求LCA),然后对于每个x,记录下每条边有多少原长超过x的路径经过(树上差分O(n)),再遍历一遍图,看看有没有满足条件的边(标记的经过路径数等于总共路径数量并且边权大于等于最长路径-x),做出判断。

    注意事项:卡常。二分的右边界设置为最长路径。

    AC代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<iomanip>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn=3e5+7;
    int n,m,p[maxn],a[maxn],b[maxn],q[maxn],d[maxn],f[maxn],dp[maxn][21],dis[maxn],len[maxn],lca[maxn],l,r,maxlen,cnt;
    struct node{
    	int v,next,value;
    }e[maxn*2]; 
    void insert(int u,int v,int w){
    	cnt++;
    	e[cnt].v=v;
    	e[cnt].value=w;
    	e[cnt].next=p[u];
    	p[u]=cnt;
    }
    void dfs(int u,int fa,int dep,int w){
    	f[u]=fa;
    	d[u]=dep;
    	dis[u]=w;
    	dp[u][0]=fa;
    	for(int i=1;(1<<i)<dep;i++){
    		dp[u][i]=dp[dp[u][i-1]][i-1];
    	}
    	for(int i=p[u];i!=-1;i=e[i].next){
    		if(e[i].v==fa) continue;
    		dfs(e[i].v,u,dep+1,w+e[i].value);
    	}
    }
    int getlca(int x,int y){
    	if(d[x]<d[y]) swap(x,y);
    	int t=d[x]-d[y];
    	for(int i=20;i>=0;i--){
    		if(t&(1<<i)) x=dp[x][i];
    	}
    	if(x==y) return x;
    	for(int i=20;i>=0;i--){
    		if(dp[x][i]!=dp[y][i]){
    			x=dp[x][i];
    			y=dp[y][i];
    		}
    	}
    	return dp[x][0];
    }
    void dfs2(int u){
    	for(int i=p[u];i!=-1;i=e[i].next){
    		if(e[i].v==f[u]) continue;
    		dfs2(e[i].v);
    		q[u]+=q[e[i].v];
    	}
    }
    bool check(int x){
    	memset(q,0,sizeof(q));
    	int k=maxlen-x,cnt2=0;
    	for(int i=1;i<=m;i++){
    		if(len[i]>x){
    			q[a[i]]++,q[b[i]]++,q[lca[i]]-=2;
    			cnt2++;
    		}
    	}
    	dfs2(1);
    	for(int i=2;i<=n;i++){
    		if(q[i]==cnt2&&dis[i]-dis[f[i]]>=k) return true;
    	}
    	return false;
    }
    int main(){
    	memset(p,-1,sizeof(p));
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<n;i++){
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		insert(u,v,w);
    		insert(v,u,w);
    	}
    	for(int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]);
    	dfs(1,-1,1,0);
    	for(int i=1;i<=m;i++){
    		lca[i]=getlca(a[i],b[i]);
    		len[i]=dis[a[i]]+dis[b[i]]-2*dis[lca[i]];
    		r=max(r,len[i]);
    	}
    	l=0;
    	maxlen=r;
    	while(l<r){
    		int mid=(l+r)/2;
    		if(check(mid)) r=mid;
    		else l=mid+1;
    	}
    	printf("%d",l);
        return 0;
    }
    

    //NOIP2015提高组Day2 t3

  • 相关阅读:
    [禅悟人生]如春风般吹开他人冬眠的良心
    [禅悟人生]先学做人, 再学做佛
    [禅悟人生]悟得自性则天地开阔
    [禅悟人生]"执著"是自缚的茧
    [禅悟人生]时常自省, 扫却心中尘埃
    [禅悟人生]心不动才能真正认清自己
    [禅悟人生]自卑裹足不前, 就无法成就自己
    [禅悟人生]有自知之明, 在深浅之间权衡做人
    禅悟人生目录
    [禅悟人生]认识自己才能了解外部世界
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/14905949.html
Copyright © 2020-2023  润新知