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

    [Noip2015]运输计划

    一.前言

    ​ 阿这……我已经尽量把常数些小了,题目光明正大地卡常,sort自身就很魔幻……题目链接

    二.思路

    ​ 快快乐乐二分做法。可以直接二分时间,是满足性质的。对于每一个猜想mid?反正进行check的时候找出所有的时间大于 mid 的计划,然后找出有哪些道路(长度设为 u)是同时被所有计划经过,并且删掉以后满足 (mid+u>=计划时间_{max}),就很暴力……

    ​ 关于道路经过数……可以差分做一个自下往上的前缀和(一个点被经过的次数也是点连父亲的那条边被经过的次数),将计划起终点的前缀和 +1 ,在 lca 处减 2,做前缀和就可以得到经过次数(推荐手玩理解)

    ​ 做前缀和的时候顺便判删边就好。

    三.CODE

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<fstream>
    #include<cmath>
    #include<cstring>
    using namespace std;
    int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
    	return res*f;
    }
    const int MAXN=300005;
    int n,m,ans=1;
    int head[MAXN],ne[2*MAXN],to[2*MAXN],dis[2*MAXN],tot;
    void add(int x,int y,int z){to[++tot]=y,dis[tot]=z,ne[tot]=head[x],head[x]=tot;}
    int dep[MAXN],f[MAXN][20],fdis[MAXN][20];
    void dfs(int x,int fa){
    	dep[x]=dep[fa]+1;
    	f[x][0]=fa;
    	for(int i=1;i<20;++i)
    		f[x][i]=f[f[x][i-1]][i-1],fdis[x][i]=fdis[x][i-1]+fdis[f[x][i-1]][i-1];
    	for(int i=head[x];i;i=ne[i]){
    		if(to[i]==fa)continue;
    		fdis[to[i]][0]=dis[i];
    		dfs(to[i],x);
    	}
    }
    inline int LCA(int x,int y,int &u){
    	int cnt=0;
    	if(dep[x]<dep[y])swap(x,y);
    	for(int i=19;i>=0;--i)if(dep[f[x][i]]>=dep[y])cnt+=fdis[x][i],x=f[x][i];
    	if(x==y){
    		u=x;
    		return cnt;
    	}
    	for(int i=19;i>=0;--i)if(f[x][i]!=f[y][i])
    	cnt+=fdis[x][i]+fdis[y][i],x=f[x][i],y=f[y][i];
    	u=f[x][0];
    	return cnt+fdis[x][0]+fdis[y][0];
    }
    struct road{
    	int s,t,alf,lca;
    }r[MAXN];
    bool cmp(road x,road y){
    	return x.alf>y.alf;
    }
    int sum[MAXN],u,cnt;
    bool fl;
    void solve(int x,int fa){
    	for(int i=head[x];i;i=ne[i]){
    		if(to[i]==fa)continue;
    		solve(to[i],x);
    		sum[x]+=sum[to[i]];
    		sum[to[i]]=0;
    	}
    	if(sum[x]==cnt&&x!=1&&fdis[x][0]>=u)fl=1;
    }
    bool check(int x){
    	u=r[1].alf-x,cnt=0;
    	for(int i=1;i<=m;++i){
    		if(r[i].alf<=x)break;
    		cnt++,sum[r[i].s]++,sum[r[i].t]++,sum[r[i].lca]-=2;
    	}
    	fl=0;
    	solve(1,0);sum[1]=0;
    	return fl;
    }
    int main(){
    	n=read();m=read();
    	for(int i=1,x,y,z;i<n;++i){
    		x=read();y=read();z=read();
    		add(x,y,z);add(y,x,z);
    	}
    	dfs(1,0);
    	for(int i=1;i<=m;++i){
    		r[i].s=read();r[i].t=read();
    		r[i].alf=LCA(r[i].s,r[i].t,r[i].lca);
    	}
    	sort(r+1,r+m+1,cmp);
    	int L=0,R=r[1].alf;
    	while(L<=R){
    		int mid=(L+R)>>1;
    		if(check(mid)){
    			ans=mid,R=mid-1;
    		}
    		else L=mid+1;
    	}
    	printf("%d",ans);
    	return 0;
    } 
    
  • 相关阅读:
    判断无向图G是否连通
    图的深度优先搜索与广度优先搜索
    整数变换问题
    按层次遍历二叉树
    交叉链表
    二元查找树转换成一个排序的双向链表
    简单计算器的实现
    二叉树宽度的计算
    BMP文件的读取与显示
    约瑟夫环问题
  • 原文地址:https://www.cnblogs.com/clockwhite/p/13505526.html
Copyright © 2011-2022 走看看