JOISC2017C 手持ち花火
Link
首先二分答案\(v\)。
考虑这样一个结论:最优解一定是\(K\)不动,其它人往\(K\)这里跑,跑到了之后先不点燃,等\(K\)的位置上每有一个人的火熄灭就点燃下一个。
那么如果我们能够点燃区间\([L,R]\)的所有烟火,就需要存在一个扩展路径上的所有\(l,r\)满足:
移项得
设\(a_i=x_i-2vTi\),上式就是\(a_l\ge a_r\)
因此,存在一种点燃方案,等价于存在\(p_1\cdots p_n,P_1\cdots 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_l\ge a_r\)。
假如我们现在有区间\([l,r]\),我们要对它扩展。
考虑如何扩展\(l\),若存在\(i\),使得
1:\(a_i\ge a_l\)。
2:\(\forall j\in[i,l],a_j\ge a_r\)。
条件2显然是必须满足的,条件一是为了使接下来\(r\)一定更远。
那么我们就可以进行一次把左端点扩展到\(i\)的扩展。
每次扩展时找到最右边的能扩展的\(i\)即可。
扩展\(r\)也是同样的操作。
如果某次我们\(l,r\)都没有扩展,并且都是因为条件2不满足,那么这个\(v\)一定不合法。
如果是因为条件1不满足,我们无能为力。
接下来考虑如何解决这个问题。
我们求出\(a_1\sim 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。
我们注意到,当\(vT\ge10^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\n", ans);
}