[loj2392]烟花棒
显然,有以下三个性质(思路):
1.烟花传递总是在烟花将要燃尽时将烟花恰传给另一个人
2.烟花不燃烧的人总是向烟花正在燃烧的人靠拢,并且重合后会一直跟着(燃尽时替上)
3.烟花正在燃烧的人总是向下一个"跟着"的人靠拢(等着不如接上后返回)
此时,过程完全由"跟着"的顺序决定(思考一下如何决定?),也即以$k$为中心向两侧扩展
再考虑相对位置,不难发现扩展$x$时,其与烟花正在燃烧的人的距离总是$\begin{cases}a_{x+1}-a_{x}&(x<k)\\a_{x}-a_{x-1}&(x>k)\end{cases}$(与另一侧扩展的人和扩展的顺序均无关),具体可以归纳证明
二分枚举答案$v$,并维护当前烟花剩余可燃烧时间$t$(初始为$k$),问题即变为:
有两个队列(依次存储两侧拓展的距离),每次任选一个队列,记其顶部元素为$d$,若$t\ge \frac{d}{2v}$则可以将$t$变为$t-\frac{d}{2v}+k$并删除该顶部元素,判断是否存在一种删除顺序可以删光两个队列
从两个队列中取出最短的非空前缀满足$\sum (k-\frac{d}{2v})\ge 0$,若可以取该前缀(即要求$t\ge lim$,$lim$可以求出)显然总可以直接取掉,重复此过程(直至均不存在这样的前缀或均无法取)
若是因后者而结束,显然问题即无解
若是因前者而结束,即此时两个队列中任意非空前缀和均小于0,不妨先得到最终的$t$并考虑逆过程(即过程中的$k$和$\frac{d}{2v}$交换一下、队列翻转一下),仍可以使用上述贪心
此时的后缀和即是原来的前缀和取相反数,总大于等于0,也即不会出现"不存在这样的前缀"的情况
由此,单次判定复杂度为$o(n)$,总复杂度为$o(n\log n)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define ll long long 5 #define fi first 6 #define se second 7 int n,t,k,x[N]; 8 vector<int>vl,vr; 9 vector<pair<ll,ll> >Vl,Vr; 10 bool check(int v){ 11 vl.clear(),vr.clear(),Vl.clear(),Vr.clear(); 12 for(int i=k;i>1;i--)vl.push_back(x[i]-x[i-1]); 13 for(int i=k+1;i<=n;i++)vr.push_back(x[i]-x[i-1]); 14 15 int lst=-1; 16 ll lim=0,sum=0; 17 for(int i=0;i<vl.size();i++){ 18 lim=max(lim,vl[i]-sum),sum+=v-vl[i]; 19 if (sum>=0){ 20 Vl.push_back(make_pair(lim,sum)); 21 lim=sum=0,lst=i; 22 } 23 } 24 reverse(vl.begin(),vl.end()); 25 for(int i=0;i<=lst;i++)vl.pop_back(); 26 27 lst=-1,lim=sum=0; 28 for(int i=0;i<vr.size();i++){ 29 lim=max(lim,vr[i]-sum),sum+=v-vr[i]; 30 if (sum>=0){ 31 Vr.push_back(make_pair(lim,sum)); 32 lim=sum=0,lst=i; 33 } 34 } 35 reverse(vr.begin(),vr.end()); 36 for(int i=0;i<=lst;i++)vr.pop_back(); 37 38 ll ans=v; 39 for(int i=0,j=0;(i<Vl.size())||(j<Vr.size());) 40 if ((i<Vl.size())&&(Vl[i].fi<=ans))ans+=Vl[i++].se; 41 else{ 42 if ((j<Vr.size())&&(Vr[j].fi<=ans))ans+=Vr[j++].se; 43 else return 0; 44 } 45 for(int i=0;i<vl.size();i++)ans+=v-vl[i]; 46 for(int i=0;i<vr.size();i++)ans+=v-vr[i]; 47 if (ans<0)return 0; 48 49 Vl.clear(),Vr.clear(); 50 lst=-1,lim=sum=0; 51 for(int i=0;i<vl.size();i++){ 52 lim=max(lim,v-sum),sum+=vl[i]-v; 53 if (sum>=0){ 54 Vl.push_back(make_pair(lim,sum)); 55 lim=sum=0,lst=i; 56 } 57 } 58 59 lst=-1,lim=sum=0; 60 for(int i=0;i<vr.size();i++){ 61 lim=max(lim,v-sum),sum+=vr[i]-v; 62 if (sum>=0){ 63 Vr.push_back(make_pair(lim,sum)); 64 lim=sum=0,lst=i; 65 } 66 } 67 68 for(int i=0,j=0;(i<Vl.size())||(j<Vr.size());) 69 if ((i<Vl.size())&&(Vl[i].fi<=ans))ans+=Vl[i++].se; 70 else{ 71 if ((j<Vr.size())&&(Vr[j].fi<=ans))ans+=Vr[j++].se; 72 else return 0; 73 } 74 return 1; 75 } 76 int main(){ 77 scanf("%d%d%d",&n,&k,&t),t<<=1; 78 for(int i=1;i<=n;i++)scanf("%d",&x[i]); 79 if (x[n]==0){ 80 printf("0\n"); 81 return 0; 82 } 83 int l=0,r=(x[n]-1)/t+1; 84 while (l<r){ 85 int mid=(l+r>>1); 86 if (check(mid*t))r=mid; 87 else l=mid+1; 88 } 89 printf("%d\n",l); 90 return 0; 91 }