浅谈队列:https://www.cnblogs.com/AKMer/p/10314965.html

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3141

很好的一道单调队列题……

先把\(0\)变成\(-1\),然后\(sum_i\)表示\([i,n]\)的后缀和。

首先考虑子段和最大值最小是多少。

首先,答案最小不会小于\(\lceil \frac{|sum_1|}{m}\rceil\)

其次,假设相邻的\(1\)或\(-1\)全部合并在一起变成\(0\)抵消掉(因为不管放在哪一段都无影响),留下\(|sum_1|\)个\(1\)或\(-1\),直接均分,刚好可以使最大值是\(\lceil \frac{|sum_1|}{m}\rceil\),所以答案就是这个玩意儿。

但是当\(sum_1\)等于\(0\)且后缀和为\(0\)的\(pos\)个数小于\(m\)时答案就是\(1\),因为分不出那么多段。

在知道答案后,我们再分情况讨论字典序:

当答案等于\(0\)时,直接把后缀和等于\(0\)的前一个点拎出来用单调队列搞搞就行。

当答案不等于\(0\),我们考虑假设第\(i\)段结尾的是\(lst\),那么第\(i+1\)段结尾处后一个位置的后缀和\(s\)要满足\(|sum[lst+1]-s|\leqslant ans\)。

展开一下:\(sum[lst+1]-ans\leqslant s \leqslant sum[lst+1]+ans\)

然后对于每种不同的后缀和维护一个单调队列即可。

每次根据上面的等式枚举下一个\(s\),然后对于子问题\(\lceil\frac{|s|}{m-i}\rceil\)不会超过全局问题的答案,并且后面够分\(m-i\)段,就把他丢到对应的单调队列里面。然后对于每个单调队列的队头拿出来比较选最好的一个就行了。

为啥要开多个单调队列而不是用一个单调队列呢?因为对于每个子问题,所满足的后缀和\(s\)并不一样,不能用当前子问题的\(s'\)把\(s''\)的队头弹掉了,万一下一次只支持\(s''\)而不支持\(s'\)并且之前被你弹掉的那个就是答案呢?

剩下的细节见代码吧。

时间复杂度:\(O(n\sqrt{n})\)

空间复习度:\(O(n)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std; const int maxn=1e6+6; int n,m,ans,num,val;
int id[maxn],like[maxn],sum[maxn],cnt[maxn];
int tot[maxn],tmp[maxn],list[maxn],stk[maxn]; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} struct Queue {
int head,tail; void push_back(int pos) {
while(head!=tail&&id[list[tail-1]]>id[pos])tail--;
list[tail++]=pos;
} int front() {return list[head];} void pop_front() {if(head!=tail)head++;} bool empty() {return head==tail;}
}alpha[maxn<<1],*q=alpha+maxn; struct Stack {
int tp,cnt; void push_back(int pos) {cnt++;stk[++tp]=pos;} bool empty() {return !cnt;} int top() {return stk[tp];} void pop() {tp--;cnt--;}
}beta[maxn<<1],*s=beta+maxn; int main() {
n=read(),m=read();
for(int i=1;i<=n;i++)
id[i]=read(),like[i]=read();
for(int i=n;i;i--) {
like[i]-=(!like[i]);
tmp[i]=sum[i]=sum[i+1]+like[i];
cnt[i]=cnt[i+1]+(!sum[i]);
}
ans=sum[1]?(abs(sum[1])+m-1)/m:cnt[1]<m;
if(!ans) {
for(int i=1,j=2;i<m;i++) {
for(;j<=n&&cnt[j+1]>=m-i;j++)
if(!sum[j+1])q[0].push_back(j);
int res=q[0].front();q[0].pop_front();
printf("%d ",id[res]);
}
}
else {
sort(tmp+1,tmp+n+1);
num=unique(tmp+1,tmp+n+1)-tmp-1;
for(int i=1;i<=n;i++)
tot[lower_bound(tmp+1,tmp+num+1,sum[i])-tmp]++;
for(int i=1;i<=num;i++) {
s[tmp[i]].tp=val;
q[tmp[i]].head=q[tmp[i]].tail=val;
val+=tot[i];
}
int lst=0;id[n+1]=n+1;
for(int i=n;i>1;i--)s[sum[i]].push_back(i-1);
for(int i=1;i<m;i++) {
int res=n+1;
for(int j=sum[lst+1]-ans;j<=sum[lst+1]+ans;j++) {
if((abs(j)+m-i-1)/(m-i)>ans)continue;
while(!s[j].empty()&&n-s[j].top()>=m-i) {
if(s[j].top()>lst)q[j].push_back(s[j].top());
s[j].pop();
}
while(!q[j].empty()&&q[j].front()<=lst)q[j].pop_front();
if(!q[j].empty()&&id[q[j].front()]<id[res])res=q[j].front();
}
lst=res;printf("%d ",id[res]);
}
}
printf("%d\n",id[n]);
return 0;
}

BZOJ3141:[HNOI2013]旅行的更多相关文章

  1. bzoj3141: [Hnoi2013]旅行

    Description   Input 第 一行为两个空格隔开的正整数n, m,表示旅行的城市数与旅行所花的月数.接下来n行,其中第 i行包含两个空格隔开的整数Ai和Bi,Ai表示他第i个去的城市编号 ...

  2. BZOJ3141 Hnoi2013 游走 【概率DP】【高斯消元】*

    BZOJ3141 Hnoi2013 Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点 ...

  3. 3141: [Hnoi2013]旅行 - BZOJ

    Description Input 第一行为两个空格隔开的正整数n, m,表示旅行的城市数与旅行所花的月数.接下来n行,其中第 i行包含两个空格隔开的整数Ai和Bi,Ai表示他第i个去的城市编号.Bi ...

  4. HNOI2013旅行

    一道欺负我智商的题... 本来想打单调队列优化dp的,结果看到算法标签就点了此题 洛谷题面 首先你要理解题意,蒟蒻理解了好久.它就是说,给你一个由1和-1组成的数列,让你分成m段,并让这m段区间和最大 ...

  5. 【LG3229】[HNOI2013]旅行

    题面 洛谷 题解 勘误:新的休息点a需要满足的条件2为那一部分小于等于ans 代码 \(100pts\) #include <iostream> #include <cstdio&g ...

  6. Hnoi2013题解 bzoj3139~3144

    话说好久没写题(解)了.. 先贴份题解:http://wjmzbmr.com/archives/hnoi-2013-%E9%A2%98%E8%A7%A3/(LJ神题解..Lazycal表示看不懂..) ...

  7. [HNOI2013]题解

    代码在最后 [HNOI2013]比赛 记忆化搜索 把每一位还需要多少分用\(27\)进制压进\(long\) \(long\),\(map\)记忆化一下即可 [HNOI2013]消毒 先考虑在二维平面 ...

  8. # HNOI2012 ~ HNOI2018 题解

    HNOI2012 题解 [HNOI2012]永无乡 Tag:线段树合并.启发式合并 联通块合并问题. 属于\(easy\)题,直接线段树合并 或 启发式合并即可. [HNOI2012]排队 Tag:组 ...

  9. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

随机推荐

  1. linux性能调分析及调优

    转:https://blog.csdn.net/luokehua789789/article/details/53007456 Linux 性能分析以及调优介绍 写在前面:计算机要解决的基本问题之一是 ...

  2. Struts2笔记02——Struts2 概述(转)

    原始内容:https://www.tutorialspoint.com/struts_2/basic_mvc_architecture.htm Struts2是基于MVC设计模式的一种流行.成熟的We ...

  3. MySQL-版本及服务介绍

    一.MySQL各版本 1.MySQL产品 下载地址:https://www.mysql.com/downloads/ Oracle MySQL Cloud Service(commercial) 商业 ...

  4. Python编程-多进程二

    7.进程间通信(IPC)方式二:管道 (1)创建管道的类: Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象 ...

  5. 使用awk来提取内容

    1.提取gff文件中的HLA基因的相关bed文件. gff的格式: zcat *gz|gawk 'BGIN{FS="\t";OFS="\t"}$3==" ...

  6. Java中系统时间的获取_currentTimeMillis()函数应用解读

    快速解读 System.currentTimeMillis()+time*1000) 的含义 一.时间的单位转换 1秒=1000毫秒(ms) 1毫秒=1/1,000秒(s)1秒=1,000,000 微 ...

  7. python里两种遍历目录的方法

    os.walk 函数声明:os.walk(top,topdown=True,onerror=None) (1)参数top表示需要遍历的顶级目录的路径. (2)参数topdown的默认值是“True”表 ...

  8. C++11 里lambda表达式的学习

    最近看到很多关于C++11的文档,有些是我不怎么用到,所以就略过去了,但是lambda表达式还是比较常用的,其实最开始学习python的时候就觉得lambda这个比较高级,为什么C++这么弱.果然C+ ...

  9. ActiveMQ部署和503的错误

    最近部署ActiveMQ的时候,发现有的服务器可以打开后台管理网址,有的服务器无法打开,Jetty报503 Service Unavailable. 搞了很久终于发现了问题,现将部署和解决过程做笔记如 ...

  10. Excel 如何复制粘贴一整行

    在某些时候,我们需要重复性的录入一些信息,而且表头都是一样的,一直拉列宽是相当令人烦躁的事情. 所以,就想可以直接复制黏贴出一整行,包括行的各个列宽. 具体操作如图: ——>首先,一整行复制, ...