点击题号跳转

A2587    B2151    C4672    D4431   E3231

F3706    G3183    H3692    I2665    J1353

A.Toys回到顶部

题意

贝西生日到了,他想庆祝D([1,10^5])天,第i天需要Ti([1,50])个玩具庆祝,1个玩具Tc([1,60])美元,玩具使用过了会变脏,消毒后可以再次使用

消毒方式1:1个玩具消毒N1([1,D])天,花费C1([1,60])美元

消毒方式2:1个玩具消毒N2([1,D])天,花费C2([1,60])美元

问怎么计划使得花费最少?

PS:庆祝273年,牛逼

题解

分析一下,设最小花费是f(x),x为总共买了多个玩具

显然f(x-1)>=f(x)<=f(x+1),少买或多买1个玩具会使得f变大

存在这样一种情况,买了太少的玩具使得怎样都不能满足计划,可设f(x)=inf

单峰函数存在最小值,可用三分解决,这里三分有一个细节

三分lmid=(l+r)/2,rmid=(lmid+r)/2,当lmid=rmid相等时,r-l<=2,如果移动r=rmid,假设答案恰好在r,那么完蛋

也就是说,我们需要保证r-l>2的时候才继续三分,最后[l,r]区间重新算一遍

那么问题就变成已知买了x个玩具求最小花费

显然可以贪心,我们让N1<N2,C1>C2,如果花费时间长还贵,完全可以让C2=C1

那么先用花费时间长N2,再用花费时间短的N1,还不行用x,再不行无解

该如何实现呢?

开3个双端队列,表示等待被消毒,可以用短时间消毒,可以用长时间消毒,代码1,复杂度O(Σalogn),2s接近tle

我们发现第一种方法贪心,每次放进入a[i]个待消毒,Σa复杂度很高

如何优化呢,设q[i]表示第i天剩下的,假设当前处理第i天,i-N2<=i-N1为两个时间节点

对于花费时间长的,维护一个指针top1,当top1<=i-N2可以用min(q[top1],x-a[i])个

对于花费时间短的,相当于top2=i-N1节点往前到top1可以用,发现区间[top1,i-N1]中可能某个为q[i]=0,如果不管,复杂度退化到n^2logn,

发现区间内的值只会不断减小到0,而且要么减成0,要么减成1个值,然后结束,那么就可以用并查集维护向前跳的节点直到top2<top1

代码2,复杂度O(nlognlogn),0.2s

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=1e5+5;
 5 int a[N];
 6 int d,n1,n2,c1,c2,tc,sum;
 7 deque<int>ti,ts,tl;
 8 //ti 需要被消毒的数量
 9 //ts 花费时间n1少的 c1元(昂贵)
10 //tl 花费事件n2长的 c2元(便宜)
11 int cal(int buy){
12     int sumc=buy*tc;
13     ti.clear();ts.clear();tl.clear();
14     for(int i=1;i<=d;i++){
15         int bought=0;
16         if(buy>=a[i])bought=a[i],buy-=a[i];
17         else bought=buy,buy=0;
18         while(!ti.empty()&&i-ti.front()>=n1){
19             ts.push_back(ti.front());
20             ti.pop_front();
21         }
22         while(!ts.empty()&&i-ts.front()>=n2){
23             tl.push_back(ts.front());
24             ts.pop_front();
25         }
26         while(bought<a[i]){
27             if(!tl.empty())tl.pop_back(),bought++,sumc+=c2;
28             else if(!ts.empty())ts.pop_back(),bought++,sumc+=c1;
29             else return 1e9;
30         }
31         for(int j=1;j<=a[i];j++)ti.push_back(i);
32     }
33     return sumc;
34 }
35 int main(){
36     while(scanf("%d%d%d%d%d%d",&d,&n1,&n2,&c1,&c2,&tc)!=EOF){
37         if(n1>n2){
38             swap(n1,n2);
39             swap(c1,c2);
40         }
41         //花费时间长而且还贵,那肯定不要了
42         if(c2>c1)c2=c1;
43         sum=0;
44         for(int i=1;i<=d;i++){
45             scanf("%d",&a[i]);
46             sum+=a[i];
47         }
48         int l=a[1],r=sum,lmid,rmid;
49         while(r-l>2){
50             lmid=(l+r)>>1;
51             rmid=(lmid+r)>>1;
52             int lval=cal(lmid),rval=cal(rmid);
53             //printf("l=%d lmid=%d rmid=%d r=%d\n",l,lmid,rmid,r);
54             //printf("lval=%d rval=%d\n",lval,rval);
55             if(rval!=1e9&&lval<rval)r=rmid;
56             else l=lmid;
57         }
58         int ans=1e9;
59         for(int i=l;i<=r;i++)ans=min(ans,cal(i));
60         printf("%d\n",ans);
61     }
62     return 0;
63 }
64 /*
65 4 1 3 49 23 59
66 13
67 37
68 26
69 37
70 */
A代码1,O(Σalogn),2s

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-11-01
  • 2021-12-04
  • 2021-12-24
  • 2021-09-16
  • 2022-12-23
  • 2021-05-07
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案