时间限制:C/C++ 3秒

空间限制:C/C++ 262144K

题目描述

小多计划在接下来的n天里租用一些服务器,所有的服务器都是相同的。接下来n天中,第i天需要\(a_i\)台服务器工作,每台服务器只能在这n天中工作m天,这m天可以不连续。

但是计划不是一成不变的,接下来有q次修改计划(修改是永久的),每次修改某一天k的需求量\(a_k\)。

小多希望知道每次修改之后,最少需要多少台服务器。

点击下载大样例

输入描述:

第一行三个正整数n,m,q,分别表示计划的天数,每台服务器能工作的天数和修改次数。

随后一行n个非负整数,第i个数字\(a_i\)​表示原计划第i天需要多少台服务器工作。

随后q行,每行两个正整数\(p_i,c_i\),表示把第\(p_i\)天需要的服务器数目改成\(c_i\)。

输出描述:

第一行输出原计划需要的最少服务器数量。

随后q行,每行输出对应的修改之后,需要的最少的服务器的数量。

输入

5 3 2

1 1 1 1 1

1 2

2 3

输出

2

2

3

样例说明

未修改时,可以租用2台服务器,分别安排给{1,4,5}和{2,3}这些天。

当第一次修改时,第一天需要两台服务器,需求变为了2 1 1 1 1,故可以安排成{1,2,3}和{1,4,5},满足所有的需求。

第二次修改时,第二天需要三台服务器,需求变为了2 3 1 1 1。可以安排三台服务器,每台服务器安排的日子分别为{1,2,3},{1,2,4}和{2,5},这样可以满足所有天的需求。

数据范围

解题思路

\(\color{red}{解法一}\) (m=1) 25分

注意到 m=1 时,\(\sum_{} ai\) 即为答案。

每次修改之后的询问,维护差值即可。

\(\color{red}{解法二}\) (n,q≤100) 10分

考虑如何利用贪心策略。

注意到如果在一开始准备了很多服务器,我们总会优先选择当前服务器使用的剩余天数最多的。

因为 ai 的值可能很大,我们可以考虑维护一个数组 bj,表示当前还可以使用j天的服务器有多少台,每次从最多的开始取。

最初服务器的数量可以二分。时间复杂度 \(\mathcal{O}(nmq \log a_i)\)。

\(\color{red}{解法三}\) (n,q≤1000) 30分

考虑如何利用贪心策略。

注意到其实对于每一种服务器需求,可以直接计算出服务器需要的最少数目即为 \(\max\left(a_i,\lceil\dfrac{\sum a_i}{m}\rceil\right)\)



考虑暴力修改改变位置及之后的前缀和数组,O(n) 计算,时间复杂度 \(\mathcal{O}(nq)\)。

参考实现代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=4e5+5;
  4. int n,m,q;
  5. long long a[N],tot[N],P[N];
  6. int main()
  7. {
  8. cin>>n>>m>>q;
  9. long long tot=0;
  10. for(int i=1;i<=n;i++)
  11. {
  12. cin>>a[i];
  13. P[i]=P[i-1]+a[i];
  14. }
  15. if(m==1|| n>=100000 ) // m==1 时直接算,或者骗分
  16. {
  17. long long tmp=P[n];
  18. cout<<(tmp+m-1)/m<<endl; //这个地方注意不能直接输出tmp,还要考虑m!=1时的骗分
  19. int p,c;
  20. for(int i=1;i<=q;i++)
  21. {
  22. cin>>p>>c;
  23. tmp=tmp+c-a[p];
  24. a[p]=c;
  25. cout<<(tmp+m-1)/m<<endl; //这个地方注意不能直接输出tmp,还要考虑m!=1时的骗分
  26. }
  27. }
  28. else // m!=1 时, q*n的做法,暴力修改变更后的前缀和
  29. {
  30. long long tmp=0,t;
  31. for(int i=1;i<=n;i++)
  32. {
  33. t=max(a[i],(P[i]+m-1)/m);
  34. tmp=max(tmp,t);
  35. }
  36. cout<<tmp<<endl;
  37. int p,c;
  38. for(int i=1;i<=q;i++)
  39. {
  40. cin>>p>>c;
  41. int temp=c-a[p];
  42. for(int k=p;k<=n;k++) //暴力O(n)更新前缀和
  43. P[k]=P[k]+temp;
  44. a[p]=c;
  45. tmp=0;
  46. for(int i=1;i<=n;i++)
  47. {
  48. t=max(a[i],(P[i]+m-1)/m);
  49. tmp=max(tmp,t);
  50. }
  51. cout<<tmp<<endl;
  52. }
  53. }
  54. return 0;
  55. }

\(\color{red}{解法四}\) (n,q≤1000) 30分

同样是想到了 \(\color{red}{解法三}\) 的贪心策略。

但是在考虑更改操作之后,序列前缀和的修改和前缀和的查询强行套上了一个树状数组,导致复杂度增加一个log,这是最开始想到的一个将问题考虑复杂化的愚蠢做法。时间复杂度 \(\mathcal{O}(nq \log n)\)。

实现代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=4e5+5;
  4. int n,m,q;
  5. long long a[N],tot[N],P[N];
  6. int lowbit(int x)
  7. {
  8. return x&(-x);
  9. }
  10. void add(int i,int x)
  11. {
  12. while(i<=n)
  13. {
  14. tot[i]=tot[i]+x;
  15. i=i+lowbit(i);
  16. }
  17. }
  18. long long getsum(int k)
  19. {
  20. long long ans=0;
  21. while(k>0)
  22. {
  23. ans=ans+tot[k];
  24. k=k-lowbit(k);
  25. }
  26. return ans;
  27. }
  28. int main()
  29. {
  30. cin>>n>>m>>q;
  31. long long tot=0;
  32. for(int i=1;i<=n;i++)
  33. {
  34. cin>>a[i];
  35. P[i]=P[i-1]+a[i];
  36. add(i,a[i]);
  37. }
  38. if(m==1|| n>=100000 ) // m==1 时直接算,或者骗分
  39. {
  40. long long tmp=P[n];
  41. cout<<(tmp+m-1)/m<<endl;
  42. int p,c;
  43. for(int i=1;i<=q;i++)
  44. {
  45. cin>>p>>c;
  46. tmp=tmp+c-a[p];
  47. a[p]=c;
  48. cout<<(tmp+m-1)/m<<endl; //这个地方注意不能直接输出tmp,还要考虑m!=1时的骗分
  49. }
  50. }
  51. else // m!=1 时, q*n*log(n) 的做法
  52. {
  53. long long tmp=0,t;
  54. for(int i=1;i<=n;i++)
  55. {
  56. t=max(a[i],(P[i]+m-1)/m);
  57. tmp=max(tmp,t);
  58. }
  59. cout<<tmp<<endl;
  60. int p,c;
  61. for(int i=1;i<=q;i++)
  62. {
  63. cin>>p>>c;
  64. add(p,c-a[p]);
  65. a[p]=c;
  66. long long tmp1=0,t1;
  67. for(int i=1;i<=n;i++)
  68. {
  69. long long sum=getsum(i);
  70. t1=max(a[i],(sum+m-1)/m);
  71. tmp1=max(tmp1,t1);
  72. }
  73. cout<<tmp1<<endl;
  74. }
  75. }
  76. return 0;
  77. }

\(\color{red}{解法五}\) (n≤400000,q≤200000) 100分

想到了 \(\color{red}{解法三}\) 的贪心策略,其实距离正解已经非常的接近了。在解法三的基础上需要进行优化的是O(n)暴力的去更新每一个修改操作之后的前缀和。

这个问题的瓶颈就在此,每次的修改操作是否需要将后面每一项前缀和都进行更新?

回到那个具有迷惑性的式子,考虑每个任务序列的最少服务器需求为:\(\color{red}{max}\) { $ \max\left(a_i,\lceil\dfrac{\sum_1^i a_i}{m}\rceil\right) $} (i=1~n)

结合方法三的证明过程,经过分析发现,实际上这个式子的答案为: \(\max\left\{\lceil\frac{\sum_{i=1}^na_i}{m}\rceil,\max_{1\le i \le n}a_i\right\}\) .

因此每次的修改操作只需要维护整个任务序列中的最大值,同时更新整个序列的和即可。

需要一个简单的数据结构来支持删除和增加一个值并维护最大值。利用 Priority_queue 或者 multiset 或其他简单数据结构可以维护这个操作。

时间复杂度 \(\mathcal{O}(n\log n+q\log n)\)。

参考实现代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define maxn 200005
  5. ll n,m,t,a[2*maxn],maxx,sum=0;
  6. priority_queue<pair<ll,ll> >q;
  7. int main()
  8. {
  9. cin>>n>>m>>t;
  10. for(ll i=1;i<=n;i++)
  11. {
  12. cin>>a[i];
  13. q.push(make_pair(a[i],i));
  14. sum+=a[i];
  15. }
  16. cout<<max(q.top().first,(sum+m-1)/m)<<endl; //注意答案要向上取整
  17. for(ll i=1;i<=t;i++)
  18. {
  19. ll p,c;
  20. cin>>p>>c;
  21. sum+=c-a[p];
  22. a[p]=c;
  23. q.push(make_pair(a[p],p));
  24. // 对于每次取出大根堆中最大元素值,要是当前最新序列的最大元素ai.
  25. while(q.top().first!=a[q.top().second]) q.pop(); //否则要删除修改之后堆中的历史节点
  26. cout<<max(q.top().first,(sum+m-1)/m)<<endl;
  27. }
  28. return 0;
  29. }
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. long long a[400010],n;
  4. multiset<long long>q;
  5. int main()
  6. {
  7. long long m,k,s=0;
  8. cin>>n>>m>>k;
  9. for(long long i=1;i<=n;i++)
  10. {
  11. cin>>a[i];
  12. s+=a[i];
  13. q.insert(a[i]);
  14. }
  15. cout<<max((s+m-1)/m,*q.rbegin())<<endl;
  16. for(long long i=1;i<=k;i++)
  17. {
  18. long long x,y;
  19. cin>>x>>y;
  20. s+=(y-a[x]);
  21. q.erase(q.find(a[x]));
  22. q.insert(a[x]=y);
  23. cout<<max((s+m-1)/m,*q.rbegin())<<endl;
  24. }
  25. return 0;
  26. }

CSP-S 2019提高组训练 服务器需求的更多相关文章

  1. 2018提高组训练Day2

    A 算法 1 对于每组询问,暴力的算出每个二次函数的取值. 时间复杂度 \(O(nq)\).期望得分 \(20\) 分. 算法 2 当 \(x>0\) 时,要求 \(a_ix^2+b_ix\) ...

  2. NOIP2018提高组省一冲奖班模测训练(六)

    NOIP2018提高组省一冲奖班模测训练(六) https://www.51nod.com/Contest/ContestDescription.html#!#contestId=80 20分钟AC掉 ...

  3. NOIP2018提高组省一冲奖班模测训练(五)

    NOIP2018提高组省一冲奖班模测训练(五) http://www.51nod.com/Contest/ContestDescription.html#!#contestId=79 今天有点浪…… ...

  4. NOIP2018提高组省一冲奖班模测训练(四)

    NOIP2018提高组省一冲奖班模测训练(四) 这次比赛只AC了第一题,而且花了40多分钟,貌似是A掉第一题里面最晚的 而且还有一个半小时我就放弃了…… 下次即使想不出也要坚持到最后 第二题没思路 第 ...

  5. NOIP2018提高组省一冲奖班模测训练(三)

    NOIP2018提高组省一冲奖班模测训练(三) 自己按照noip的方式考,只在最后一两分钟交了一次 第一题过了,对拍拍到尾. 第二题不会.考试时往组合计数的方向想,推公式,推了一个多小时,大脑爆炸,还 ...

  6. NOIP2018提高组省一冲奖班模测训练(二)

    比赛链接 NOIP2018提高组省一冲奖班模测训练(二) 今天发挥正常,昨天不在状态…… 花了很久A了第一题 第二题打了30分暴力 第三题投机取巧输出test1答案(连暴力都不知道怎么打,太弱了) 2 ...

  7. 「CSP」第一届提高组考后总结

    「CSP」第一届提高组考后总结 问题分析+反思 成绩 心态 考前心态 考时心态 考后心态 方法 心灵鸡汤... 在学习了三年之后,我们信竞迎来了初中最后一次大考,也是第一次 CSPCSPCSP 考试. ...

  8. 【GDOI2014模拟】JZOJ2020年8月14日提高组 服务器

    [GDOI2014模拟]JZOJ2020年8月14日提高组 服务器 题目 Time and Memory Limits Description 我们需要将一个文件复制到n个服务器上,这些服务器的编号为 ...

  9. noip2017爆炸记——题解&总结&反省(普及组+提高组)

    相关链接: noip2018总结 noip2017是我见过的有史以来最坑爹的一场考试了. 今年北京市考点有一个是我们学校,我还恰好被分到了自己学校(还是自己天天上课的那个教室),于是我同时报了普及提高 ...

随机推荐

  1. (CSDN 迁移) JAVA循环删除List的某个元素

    若列表中只可能存在一个则可以用简单的循环删除,不多说. 若列表中可能存在多个,尤其是可能有多个连续的需要删除,用简单循环有可能发生异常. 需要使用迭代器(Iterator),两种具体实现: 逻辑上是一 ...

  2. Intellij IDEA设置类注释和方法注释

    背景:工欲善其事必先利其器,如果不能把工具用熟练了, 感觉很是别扭. 参考:IntelliJ IDEA设置类注释和方法注释 IntelliJ IDEA 中创建类的时候,可以直接自动给类加注释的设置,以 ...

  3. .whl文件安装cuda10.0版本的pytorch1.3.0+torchvision0.4.1

    $ python3 -m venv env3$ source env3/bin/activate$ cd env3/share/python-wheels 在此找到对应的版本:https://down ...

  4. 031 SSM综合练习07--数据后台管理系统--用户详情查询

    1.用户详情查询流程分析 2.代码实现 (1)user-list.jsp页面部分代码 点击jsp页面中的详情按钮,发送请求到UserController.java <!--数据列表--> ...

  5. Delphi 开发微信公众平台 (三)- 获取微信服务器IP地址

    如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制,可以通过该接口获得微信服务器IP地址列表或者IP网段信息. 接口调用请求说明 http 请求方式: GET https:/ ...

  6. Django框架之第八篇(模型层补充)--数据库的查询与优化:only/defer,select_related与prefetch_related,事务

    在设置外键字段时需要注意: 当你使用django2.x的版本时候,在建立外键关系时,需要你手动添加几个关键点参数 models.cascade #设置级联删除 db_constraints 数据库查询 ...

  7. NAIPC 2018

    E. Prefix Free Code 大意: 给定$n$个串, 保证任意一个串都不是另一个串的前缀, 从中选出$k$个串可以拼成$\binom{n}{k}k!$种串. 给定其中一个串, 求这个串的排 ...

  8. Docker 安装入门 Centos Linux安装Docker 部署mysql

    这次购买了阿里云云服务器,并且安装了Centos 7.5 学习使用Docker, 确认版本信息 Docker 运行在 CentOS 7 上,要求系统为64位.系统内核版本为 3.10 以上. Dock ...

  9. 一文快速入门Docker

    Docker提供一种安全.可重复的环境中自动部署软件的方式,拉开了基于与计算平台发展方式的变革序幕.如今Docker在互联网公司使用已经非常普遍.本文用十分钟时间,带你快速入门Docker. Dock ...

  10. WPF 很少人知道的科技

    原文:WPF 很少人知道的科技 本文介绍不那么常见的 WPF 相关的知识. 本文内容 在 C# 代码中创建 DataTemplate 多个数据源合并为一个列表显示 使用附加属性做缓存,避免内存泄漏 使 ...