JOISC2017C 手持ち花火

Link
首先二分答案(v)
考虑这样一个结论:最优解一定是(K)不动,其它人往(K)这里跑,跑到了之后先不点燃,等(K)的位置上每有一个人的火熄灭就点燃下一个。
那么如果我们能够点燃区间([L,R])的所有烟火,就需要存在一个扩展路径上的所有(l,r)满足:

[x_l+vT(r-l)ge x_r-vT(r-l) ]

移项得

[x_l-2vTlge x_r-2vTr ]

(a_i=x_i-2vTi),上式就是(a_lge a_r)
因此,存在一种点燃方案,等价于存在(p_1cdots p_n,P_1cdots P_n),使得(p_1=P_1=k,p_n=1,P_n=n),随着(i)递增每次(p_i)减一或者(P_i)加一,并且保证(a_{p_i}ge a_{P_i})
也就是存在一个扩展路径使得路径上的每对(l,r)满足(a_lge a_r)
假如我们现在有区间([l,r]),我们要对它扩展。
考虑如何扩展(l),若存在(i),使得
1:(a_ige a_l)
2:(forall jin[i,l],a_jge a_r)
条件2显然是必须满足的,条件一是为了使接下来(r)一定更远。
那么我们就可以进行一次把左端点扩展到(i)的扩展。
每次扩展时找到最右边的能扩展的(i)即可。
扩展(r)也是同样的操作。
如果某次我们(l,r)都没有扩展,并且都是因为条件2不满足,那么这个(v)一定不合法。
如果是因为条件1不满足,我们无能为力。
接下来考虑如何解决这个问题。
我们求出(a_1sim a_{K-1})中最大值(a_{pl})(a_{K+1}sim a_n)中的最小值(pr)
显然如果我们能够扩展到([pl,pr]),那么接下来1条件一定不满足,并且说明过程中2条件都满足,这一部分是合法的。
我们再考虑将左端点(l)设为(1),右端点(r)设为(n),向内扩展。扩展过程就是上面的反过来。
如果我们还能扩展到([pl,pr]),那么说明过程中2条件都满足,也就是这个(v)合法。
我们要计算的有(vTi),这个东西显然可能爆long long。
我们注意到,当(vTge10^9)时,一定可以点燃所有烟花,所以一开始把二分的右端点设为(frac{10^9}{T}+1)即可。

#include<bits/stdc++.h>
#define LL long long
#define mid ((l+r)>>1)
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],*iS,*iT;
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    int read(){int x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
}
using namespace IO;
const int N=100007,inf=1e9;
int n,K,T,x[N];
LL a[N];
int check(int v)
{
    int i,l,r,L,R,ql,qr,f;
    for(i=1;i<=n;++i) a[i]=1ll*x[i]-2ll*T*v*i;
    if(a[1]<a[n]) return 0;
    for(ql=qr=K,i=K-1;i;--i) if(a[i]>=a[ql]) ql=i;
    for(i=K+1;i<=n;++i) if(a[i]<=a[qr]) qr=i;
    for(l=r=K;l^ql||r^qr;)
    {
	f=0,L=l,R=r;
        for(;L>ql&&a[L-1]>=a[r];) if(a[--L]>=a[l]) break;
        if(L<l&&a[L]>=a[l]) f=1,l=L;
        for(;R<qr&&a[R+1]<=a[l];) if(a[++R]<=a[r]) break;
	if(R>r&&a[R]<=a[r]) f=1,r=R;
        if(!f) return 0;
    }
    for(l=1,r=n;l^ql||r^qr;)
    {
	f=0,L=l,R=r;
        for(;L<ql&&a[L+1]>=a[r];) if(a[++L]>=a[l]) break;
        if(L>l&&a[L]>=a[l]) f=1,l=L;
	for(;R>qr&&a[R-1]<=a[l];) if(a[--R]<=a[r]) break;
	if(R<r&&a[R]<=a[r]) f=1,r=R;
        if(!f) return 0;
    }
    return 1;
}
int main()
{
    n=read(),K=read(),T=read();int i,l=0,r=inf/T+1,ans;
    for(i=1;i<=n;++i) x[i]=read();
    while(l<=r) check(mid)? ans=mid,r=mid-1:l=mid+1;
    printf("%d
", ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12342182.html