1 条题解
-
0
自动搬运
来自洛谷,原作者为

是个汉子
世人万千种,浮云莫去求。斯人若彩虹,遇上方知有。搬运于
2025-08-24 21:46:43,当前版本为作者最后更新于2020-11-22 10:58:44,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这个题用到了单调队列的优化,但是精髓绝不止在于此。+_+
Solution
我们先不管字典序怎么办,想想怎么求最小的
首先,因为要的是绝对值,那么考虑将 化为 ,也就是将没有景点的城市看作会减少一个景点的城市(雾
设 表示城市 是否有景点,有为 ,否则为 , 表示 到 的后缀和,也就是 ,那么可以证明答案最小不小于 。
如果全部是 或 ,显然平均的将 分为 份最好;如果同时有 ,将相邻的两个 合并可以得到 即抵消,最后还是剩下 个 或 ,就和第一种一样了。
但是还是有一种特殊情况的,当 且 的 的数量不足 个,此时答案就是 ,因为分不了那么多段。
既然 已经能够求出来了,那么就可以回来看看字典序
将 和 分开考虑。
当 时,即 并且 的 的数量 ,那么就在这些 中跑一遍单调队列即可。
再具体就是维护队列里 从小到大,按照输入顺序入队 ,找到字典序最小的 个。(因为第 个城市必定是 )
当 时,我们假设第 段结尾的是 ,那么 段的结尾后一个位置的后缀和 需要满足 $|sum_{lst}-s|\leq ans\Rightarrow sum_{lst}-ans\leq s\leq sum_{lst}+ans$ 。
那么选择按 分类,对每一种 开一个单调队列,维护队列里 单调递增,选的时候从 到 枚举选择,注意我们还需要判断 。
注意:这题卡空间,所以要手动写队列。(●ˇ∀ˇ●)
Code
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define B N using namespace std; const int N=500010; template<typename T>void read(T &x){ x=0;bool f=0; char ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=1;ch=getchar();} while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();} if(f) x=-x; } struct node{ int v,id; }; int cnt; struct Node{ node p; int pre,nxt; }tr[N<<1]; int newcode(node p,int pre,int nxt){tr[++cnt]=(Node){p,pre,nxt}; return cnt;} struct queue{ int siz,hd,tl; bool empty(){return !siz;} void push_front(node p){ if(!siz) hd=tl=newcode(p,0,0); else tr[hd].pre=newcode(p,0,hd),hd=tr[hd].pre; ++siz; } void push_back(node p){ if(!siz) hd=tl=newcode(p,0,0); else tr[tl].nxt=newcode(p,tl,0),tl=tr[tl].nxt; ++siz; } void pop_front(){siz--,hd=tr[hd].nxt;} void pop_back(){siz--,tl=tr[tl].pre;} node front(){return tr[hd].p;} node back(){return tr[tl].p;} }q[N<<1]; int tot,head[N<<1],nxt[N<<1]; node to[N<<1]; void add(int u,node p){ to[++tot]=p; nxt[tot]=head[u]; head[u]=tot; } int n,m,lst,ct[N],sum[N],rest[N];//rest表示休息点的后缀和 sum表示相加的后缀和 int calc(){ int ans=0; for(int i=n;i>=1;i--){ if(!sum[i]) ans++,rest[i]=1; rest[i]+=rest[i+1]; } if(ans>=m) return 0; else return 1; } void push(int u,node p){ while(!q[u].empty()&&p.v<q[u].back().v) q[u].pop_back(); q[u].push_back(p); } node calc(int u,int lim){ for(int i=head[u];i&&to[i].id<=lim;i=nxt[i]) push(u,to[i]),head[u]=i; while(!q[u].empty()&&q[u].front().id<=lst) q[u].pop_front(); return q[u].empty()?(node){N,N}:q[u].front(); } node min(node a,node b){return a.v<b.v?a:b;} int main(){ read(n); read(m); for(int i=1;i<=n;i++) read(ct[i]),read(sum[i-1]),sum[i-1]=(sum[i-1])?1:-1; for(int i=n-1;i>=0;i--) sum[i]+=sum[i+1]; for(int i=n;i>=1;i--) add(sum[i]+B,(node){ct[i],i}); int S=sum[0],d=(S!=0)?(int)ceil(1.0*abs(S)/(1.0*m)):calc(); if(d){ for(int i=1;i<m;i++){ node ans=(node){N,N}; for(int j=S-d+B;j<=S+d+B;j++){ if(ceil(abs(1.0*j-B)/(1.0*m-i))<=d) ans=min(ans,calc(j,n-(m-i))); } printf("%d ",ans.v); lst=ans.id,S=sum[lst]; } } else{ for(int j=1,i=head[B];j<m;j++){ for(;i&&rest[to[i].id]-1>=m-j;i=nxt[i]) push(B,to[i]); printf("%d ",q[B].front().v); q[B].pop_front(); } } printf("%d\n",ct[n]); return 0; }
- 1
信息
- ID
- 2302
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者