[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 }
View Code

 

posted @ 2021-11-12 14:28  PYWBKTDA  阅读(139)  评论(0编辑  收藏  举报