zoukankan      html  css  js  c++  java
  • [IOI2016]shortcut 题解

    洛谷地址//UOJ地址

    题意描述

    在一个 (n) 个点的链上,每个点下面挂了一条长度为 (d_i) 的边
    现在可以添加一条边连接两个链上的点,这条边长度为 (c),现在要使直径最小

    题解

    (IOI) 题目被 (wc) 老师五分钟带过……
    首先求出每个点到第一个点的距离,设其为 (L),那么每两点间的最短路就是 (L_j-L_i+d_i+d_j)
    若在 (a,b) 两点间添加一条边,那么 (i,j) 间的距离就变成 (|L_i-L_a|+|L_j-L_b|+d_i+d_j)
    那么这个时候我们想找到 (min(式1,式2)) 的最大值
    这个时候想到了二分一个 (M),然后去验证这个 (M)是否可行
    对于加边前,两点距离就已经小于等于 (M) 的,我们就没必要管他,重点是 (L_j-L_i+d_i+d_j>M)
    然后我们看一下式二,既然式一不能满足限制,那么式二肯定要满足限制,也就是 (|L_i-L_a|+|L_j-L_b|+d_i+d_j leq M)
    然后就考虑爆拆绝对值,分别求最小值再求交
    然后老师就默认大家都会,没有讲了
    其实呢,老师的意思是,把绝对值爆拆之后,把含 (a,b) 的项移到右边去,构成约束,找到最紧的约束,这样就可以判
    于是我们来拆开这个式子看一下(分类中的小于等于和大于等于就省略掉等于了)

    • (L_j>L_b)(L_i>L_a)

    [L_i-L_a+L_j-L_b+d_i+d_j leq M ]

    [L_i+L_j+d_i+d_j-M leq L_a+L_b ]

    • (L_j>L_b)(L_i<L_a)

    [L_a-L_i+L_j-L_b+d_i+d_j leq M ]

    [L_j-L_i+d_i+d_j-M leq L_b-L_a ]

    [L_i-L_j-d_i-d_j+M geq L_a-L_b ]

    • (L_j<L_b且L_i>L_a)

    [L_i-L_a+L_b-L_j+d_i+d_j leq M ]

    [L_i-L_j+d_i+d_j-M leq L_a-L_b ]

    • (L_j<L_b且L_i<L_a)

    [L_a-L_i+L_b-L_j+d_i+d_j leq M ]

    [-L_i-L_j+d_i+d_j-M leq -L_a-L_b ]

    [L_i+L_j-d_i-d_j+M geq L_a+L_b ]


    啊,(mk) 排版有待提升
    然后我们得到了一系列约束,这个时候我们发现很多符号不太统一,所以我们给第二、四种情况两边取负,这样的话 (L_a) 就一直会是正的(然后我们会从求最大值变成求最小值)
    我们对于四种情况,把左式求出,考虑枚举一个 (j),我们可以想到用一个线段树来找到 (L_i+d_i)(L_i-d_i) ,似乎需要两棵树(没试过)
    这样的话时间就是 (O(nlog_nlog_n)),还是过不去,我们还需要更好的
    原课件是证明了一个单调性之后使用双指针,但是我并没有看懂,于是在网上找到了一位大佬的代码做参考,使用单调队列
    然后由于我比较菜,一眼看过去并不怀疑其正确性,所以没有研究为什么单调队列就可以了
    然后我们说一下怎么判断有没有解
    我们的 (L) 数组肯定是单调不降的,这没问题,但是细心的小伙伴可能发现,有没有可能,第一种情况下,约束是最紧的,但是我们在该约束条件下找到的(a,b)并不满足该约束的前提(也就是 (L_j>L_b)(L_i>L_a)
    这是不可能的,因为如果不符合前提,那么爆拆绝对值之后的值就是负数,不是最优的
    最后,枚举一个 (a),用两组 (l,r) 分别代表两组约束,然后如果 (r1>=l1且r1>=l2)(r2)同理,成立,那么就是有解了
    具体细节可以看代码(这次没有放毒瘤码风了)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    const ll inf=((1ll<<62)-1);
    ll n,m,s[10100101],d[1010001],L,R,ans,mid;
    
    bool check(ll sam){
    	ll mx1=-inf,mx2=-inf,mn1=inf,mn2=inf,Mx=-inf,mx=d[1]-s[1];
    	ll qq[1001001],l1=n+1,r1=n,l2=1,r2=0,l=1,r=1;
    	qq[1]=1;
    	for(ll i=2;i<=n;++i){
    		if(mx+s[i]+d[i]>sam){
    			mn1=min(mn1,sam-d[i]+s[i]-mx-m);
    			mn2=min(mn2,sam-d[i]-s[i]-mx-m);
    			while(l<=r&&d[qq[l]]-s[qq[l]]+d[i]+s[i]>sam){
    				Mx=max(Mx,d[qq[l]]+s[qq[l]]);
    				++l;
    			}
    			mx1=max(mx1,-sam+d[i]+s[i]+Mx+m);
    			mx2=max(mx2,-sam+d[i]-s[i]+Mx+m);
    		}
    		mx=max(mx,d[i]-s[i]);
    		while(r>=l&&d[i]-s[i]-d[qq[r]]+s[qq[r]]>=0)--r;
    		qq[++r]=i;
    	}
    	if(mx1>mn1||mx2>mn2)return 0;
    	for(ll i=1;i<=n;++i){//寻找 a b 
    		while(l1>1&&s[l1-1]+s[i]>=mx1)--l1;
    		while(r1>=l1&&s[r1]+s[i]>mn1)--r1;
    		while(r2<n&&s[r2+1]-s[i]<=-mx2)++r2;
    		while(l2<=r2&&s[l2]-s[i]<-mn2)++l2;
    		if(l1>r1||l2>r2)continue;
    		if(!(l2>r1||l1>r2))return 1;
    	}
    	return 0;
    }
    
    int main(){
    	scanf("%lld%lld",&n,&m);
    	for(ll i=2;i<=n;++i)scanf("%lld",&s[i]);
    	for(ll i=1;i<=n;++i)s[i]+=s[i-1];
    	for(ll i=1;i<=n;++i)scanf("%lld",&d[i]);
    	L=0,R=(n+1)*(1e9);
    	while(L<=R){
    		mid=(L+R)>>1;
    		if(check(mid))ans=mid,R=mid-1;
    		else L=mid+1;
    	}
    	cout<<ans;
    }
    
  • 相关阅读:
    206. 反转链表
    917. 仅仅反转字母
    哈希表的设计
    387. 字符串中的第一个唯一字符
    Mysql重连错误
    MySQL数据库的优化
    XML基础知识
    JS中的DOM与BOM
    JS中的函数,Array对象,for-in语句,with语句,自定义对象,Prototype
    表格标签,图片标签,框架标签,表单标签
  • 原文地址:https://www.cnblogs.com/caijiLYC/p/14384106.html
Copyright © 2011-2022 走看看