其他分享
首页 > 其他分享> > 【洛谷3229】[HNOI2013] 旅行(单调队列)

【洛谷3229】[HNOI2013] 旅行(单调队列)

作者:互联网

题目链接

最小的差值

记 \(op_i=\begin{cases}1&b_i=1,\\-1&b_i=0\end{cases}\),并设 \(s_i=\sum_{k=1}^iop_k\)。

显然,最小的差值必然大于等于 \(\lceil\frac{s_n}l\rceil\)。

记 \(t=\lceil\frac{s_n}l\rceil\),如果 \(t>0\),肯定存在若干个位置满足 \(s_i\) 分别为 \(t,2t,3t,\cdots\),分别取这些位置作为段末尾,最小的差值可以取到 \(t\)。

如果 \(t=0\),若存在大于等于 \(m\) 个位置满足 \(s_i=0\) 那么最小的差值就能取到 \(0\),否则只能取到 \(1\)。

这样一来我们就确定了最小的差值(记作 \(g\)),接下来就是要在此前提下求出字典序最小的 \(q\)。

最小的字典序

因为要让字典序最小,依次考虑每一项,肯定是在合法的项中选择最小的那一项。

如果 \(g=0\),我们只能在 \(s_i=0\) 的位置中选择(假设有 \(c\) 个),只要初始将前 \(c-m\) 个元素加入单调队列,然后每加入一个元素就取出队首输出即可。

对于一般情况,假设我们当前在填第 \(i\) 项,上一段末尾位置为 \(lst\),那么对于一个位置 \(x\),需要满足以下条件:

对于前两个条件,因为 \(lst,n-m+i\) 都是单调递增的,依旧使用单调队列维护,只要每次更新 \(i\) 时加入新的合法元素,更新 \(lst\) 时弹出开头的不合法元素即可。

对于后两个条件,发现只与 \(s_x\) 有关,因此可以实际上可以对于不同的 \(s_x\) 分别开一个单调队列,每次枚举 \([s_{lst}-g,s_{lst}+g]\) 范围内的这些单调队列,用其中合法的那些更新答案。

具体实现中我们需要讨论 \(m\) 个末尾位置,对于每个位置至多需要枚举 \(2g+1\) 个单调队列,因为 \(mg≈s_n\le n\),所以总复杂度 \(O(n)\)。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 500000
#define INF (int)1e9
using namespace std;
int n,m,a[N+5],s[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc(' ');}
}using namespace FastIO;
int pre[N+5],nxt[N+5];struct Q
{
	int H,T;I void A(CI x) {W(T&&a[T]>a[x]) T=pre[T];T?(pre[x]=T,nxt[T]=x,T=x):(H=T=x);}//单调队列
	I void D(CI x) {H==x&&(pre[H=nxt[H]]=0,!H&&(H=T=0));}//弹出队首
}q[2*N+5];
int main()
{
	RI i,x,c=0;for(read(n,m),a[0]=INF,i=1;i<=n;++i) read(a[i],x),!(s[i]=s[i-1]+(x?1:-1))&&++c;
	RI g=(abs(s[n])+m-1)/m;!g&&c<m&&++g;if(!g)//如果g=0
	{
		for(c=0,i=1;i<=n;++i) !s[i]&&(a[++c]=a[i]);for(i=1;i<=c-m;++i) q[0].A(i);//加入前c-m个元素
		for(i=1;i^m;++i) q[0].A(c-m+i),write(a[q[0].H]),q[0].D(q[0].H);return write(a[n]),clear(),0;//加一个元素,输出一次队首
	}
	RI o=1,j,t,id,k=0;for(i=1;i<=n-m;++i) q[n+s[i]].A(i);for(i=1;i^m;++i)//加入前n-m个元素
	{
		q[n+s[n-m+i]].A(n-m+i),t=INF;//加入第n-m+i个元素
		for(j=max(k-g,-n);j<=min(k+g,n);++j) abs(s[n]-j)<=g*(m-i)&&t>a[q[n+j].H]&&(t=a[id=q[n+j].H]);//枚举合法的单调队列更新答案
		write(t),k=s[id];W(o<=id) q[n+s[o]].D(o),++o;//弹出开头的不合法元素
	}return write(a[n]),clear(),0;//最后的末尾必须是a[n]
}

标签:le,洛谷,队列,最小,HNOI2013,lst,3229,单调,define
来源: https://www.cnblogs.com/chenxiaoran666/p/Luogu3229.html