zoukankan      html  css  js  c++  java
  • 【BZOJ4326】[NOIP2015] 运输计划(二分+差分)

    点此看题面

    大致题意: 一棵树,每条边有一个边权,你可以选择其中一个修改为(0)。给定若干条树上路径,求最小化最长的路径长度。

    前言

    果然数据结构题做傻掉了,一开始竟然想出一个二维数点的做法 emmm(过应该是可以过的,但可能要稍微麻烦一些)

    无所畏惧两个(log),洛谷上最慢的点跑了(1.96s)。。。其实完全可以写(O(1)LCA)做到单(log),但懒得去写了,倍增它不香吗。。。

    二分

    考虑我们二分答案(x),那么对于每条长度大于(x)的路径(路径长度可以预处理),都需要修改一条该路径上的边使它长度小于等于(x)

    换言之,修改的路径必须属于所有非法路径的交集,那么必然贪心地选择交集中的最大边权。

    而如果最长的路径长度减去最大边权小于等于(x),就说明(x)是个可行答案,否则不可行。

    那么问题来了,我们该如何求出树上若干路径的交集?

    差分

    这个套路其实我在另一道题中做过:【BZOJ4424】Fairy(树上差分)

    我们求解每条边被经过的次数,把这一信息维护在它连接的子节点上。

    每次给(x,y)的点权分别加(1),给(LCA(x,y))的点权减(2),然后(dfs)一遍统计子树和即可得到每条边被经过的次数。

    如果一条边被经过的次数等于非法路径总数,就说明它是交集中的边,否则不是。

    这样一来这道题就做完了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300000
    #define LN 20
    #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
    using namespace std;
    int n,m,Mx,a[N+5],b[N+5],p[N+5],g[N+5],v[N+5],ee,lnk[N+5];
    struct edge {int to,nxt,val;}e[N<<1];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		#undef D
    }F;
    class MultiLCA
    {
    	private:
    		int d[N+5],D[N+5],f[N+5][LN+5];
    	public:
    		I void Init(CI x=1)//初始化
    		{
    			RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];
    			for(i=lnk[x];i;i=e[i].nxt) e[i].to^f[x][0]&&(D[e[i].to]=D[x]+e[i].val,
    				v[e[i].to]=e[i].val,d[e[i].to]=d[f[e[i].to][0]=x]+1,Init(e[i].to),0);
    		}
    		I int LCA(RI x,RI y)//倍增LCA
    		{
    			RI i;d[x]<d[y]&&(x^=y^=x^=y);
    			for(i=0;d[x]^d[y];++i) (d[x]^d[y])>>i&1&&(x=f[x][i]);if(x==y) return x;
    			for(i=LN;~i;--i) f[x][i]^f[y][i]&&(x=f[x][i],y=f[y][i]);return f[x][0];
    		}
    		I int Dis(CI x,CI y) {return D[x]+D[y]-(D[LCA(x,y)]<<1);}//询问树上距离
    }T;
    I void dfs(CI x=1,CI lst=0)//推标记
    {
    	for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfs(e[i].to,x),g[x]+=g[e[i].to]);
    }
    I bool Check(CI x)//验证
    {
    	RI i,t=0;for(i=1;i<=n;++i) g[i]=0;for(i=1;i<=m;++i)
    		p[i]>x&&(++g[a[i]],++g[b[i]],g[T.LCA(a[i],b[i])]-=2,++t);//差分
    	RI k=0;for(dfs(),i=2;i<=n;++i) g[i]==t&&k<v[i]&&(k=v[i]);return Mx-k<=x;//求出交集中的最大边权
    }
    int main()
    {
    	RI i,x,y,z;for(F.read(n,m),i=1;i^n;++i) F.read(x,y,z),add(x,y,z),add(y,x,z);
    	for(T.Init(),i=1;i<=m;++i) F.read(a[i],b[i]),Mx<(p[i]=T.Dis(a[i],b[i]))&&(Mx=p[i]);//预处理出每条路径长度
    	RI l=0,r=1e9,mid;W(l<r) Check(mid=l+r-1>>1)?r=mid:l=mid+1;return printf("%d
    ",r),0;//二分答案
    }
    
  • 相关阅读:
    jquery select取值,赋值操作
    数据库性能监测工具
    Redis集群搭建与简单使用
    Docker安装指南
    yum常用操作
    centos6.8 安装Python2.7后, yum出现“No module named yum”错误
    Docker使用阿里云docker镜像加速
    高并发缓存架构
    mysql主从复制-方案1
    redis队列操作
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4326.html
Copyright © 2011-2022 走看看