其他分享
首页 > 其他分享> > AT2535 手持ち花火 (Sparklers)

AT2535 手持ち花火 (Sparklers)

作者:互联网

烟花棒题解报告

在考场上推了1个半小时,但还是没有推出来。

我一开始以为那个燃烧的烟花棒只要碰到其他的烟花棒,就会点燃。(于是样例都没过)

总结一下题目的意思吧:有n个烟花棒,要让第k个烟花棒不断引然其他的烟花棒,每根烟花棒只能烧T秒(烧完就没了),只有两个烟花棒在同一个位置时才能够引燃。在同一个位置的烟花棒也可以暂时不点燃。每根烟花棒都有一个相同的速度v,可以向左和向右运动,也可以静止。
求速度至少为多少时,可以点燃所有烟花棒。(此时可能有些烟花棒已经烧完了)

题目让我们求速度的最小值,我们可以二分一个速度v,然后check(v)检验是否成立。

在check里面的部分才是真的精髓duliu

首先,每个烟花棒都有一个\(x[i]\),表示与1号烟花棒的距离。我们可以创造一个区间\([l,r]\),表示在\([l,r]\)中有一个点燃的烟花棒,可以顺带着把其他的烟花棒都引燃。

我们可以发现,左右两端的烟花棒都会向中间点燃的烟花棒靠拢。大家都知道的吧

另外,场上只能有一个点燃的烟花棒,因为在同一个位置可以不点燃,而且要让你的总时间尽可能大(因为你的速度是一定的,路程\(S=vt\)),所以就可以让其他的烟花棒跟着你跑嘛,等到快熄灭的时候在点燃它们。

综合上面两条结论,我们就可以知道点燃\([l,r]\)区间中的最后一个烟花棒,最大的消耗时间为\((r-l)*T\)(就是烧掉除\(r\)外的其他烟花棒)。

但是烟花棒全部点燃的距离就是\(x[r]-x[l]\),可以自己画个图理解一下,当你和其他烟花棒在往同一个方向跑动的时候,相对位置是不变的。

所以得出\([l,r]\)合法的条件为

\[2*v*T*(r-l) \ge x[r]-x[l]\]

拆括号,移项可得

\[x[l]-2*v*T*l \ge x[r]-2*v*T*r\]

所以我们可令\(a[i]=x[i]-2*v*T*i\),那么题目就可以转化为由区间\([k,k]\)转移到\([1,n]\)其中每一个区间都要合法。

所以一个可以拓展的区间\([l,r]\),对于\(i<l\),如果满足条件

\(1.a[i] \ge a[l]\)

\(2.\forall j \in [i,l]\),有\(a[j] \ge a[r]\)

那这个区间就可以拓展为\([i,r]\)。

通过上面的步骤,我们可以把区间\([k,k]\)拓展为一个"极大"的区间\([L,R]\)。在这个过程中,若是出现了\(a[i]\ge a[l]\),但是\(\exists j\in [i,l],a[j]<a[r]\),就可以返回\(false\)了。

第二步,我们从大区间\([1,n]\)向里面缩小。

所以一个可以缩小的区间\([l,r]\),对于\(l<i<r\),如果满足条件

\(1.a[i] \le a[l]\)

\(2.\forall j \in [l,i]\),有\(a[j] \ge a[r]\)

那么这个区间就可以缩小为\([i,r]\)。

可见缩小的过程与放大的过程是可逆的,若是可以再次得到区间\([L,R]\),那这个方案是可行的。反之,返回\(false\)。

上代码吧。

#include<bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
ll n,k,t,x[N],ans,a[N];
bool check(ll v){
    for(int i=1;i<=n;++i)a[i]=(long double)x[i]-(long double)2*v*t*i;
    if(a[1]<a[n])return 0;
    int L=k,R=k;
    for(int i=k-1;i;--i){
        if(a[i]>=a[L])L=i;
    }
    for(int i=k+1;i<=n;++i){
        if(a[i]<=a[R])R=i;
    }
    int l=k,r=k;//从中心向两边拓展
    while(l!=L||r!=R){
        int ok=0,lll=l,rrr=r;//ok判断是否能够拓展到[L,R]
        while(lll>L&&a[lll-1]>=a[r]){
            if(a[--lll]>=a[l])break;
        }
        if(lll<l&&a[lll]>=a[l])ok=1,l=lll;//判断上一句是否拓展过
        while(rrr<R&&a[rrr+1]<=a[l]){
            if(a[++rrr]<=a[r])break;
        }
        if(rrr>r&&a[rrr]<=a[r])ok=1,r=rrr;
        if(!ok)return 0;//无法拓展到[L,R],无解
    }
    l=1,r=n;//再从两端向中间缩小区间
    while(l!=L||r!=R){
        int ok=0,lll=l,rrr=r;
        while(lll<L&&a[lll+1]>=a[r]){
            if(a[++lll]>=a[l])break;
        }
        if(lll>l&&a[lll]>=a[l])ok=1,l=lll;
        while(rrr>R&&a[l]>=a[rrr-1]){
            if(a[--rrr]<=a[r])break;
        }
        if(rrr<r&&a[rrr]<=a[r])ok=1,r=rrr;
        if(!ok)return 0;
    }
    return 1;
}
int main(){
    cin>>n>>k>>t;
    for(int i=1;i<=n;++i)cin>>x[i];
    ll l=0,r=0x3f3f3f3f;//注意二分边界必须是0,因为当所有烟花棒重叠时是不需要速度的,我就错了几遍
    while(l<=r){
        ll mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

标签:点燃,Sparklers,烟花,ge,lll,区间,可以,AT2535,花火
来源: https://www.cnblogs.com/fenghuazhengmao/p/11581359.html