LGP2827 [NOIP-S 2016] 蚯蚓 学习笔记
LGP2827 [NOIP-S 2016] 蚯蚓 学习笔记
题意简述
有一个可重集,其初始有 \(n\) 个元素。每一秒,其最大的元素之一 \(x\) 将会被提取出来,被分成 \(\lfloor px \rfloor\) 和 \(x-\lfloor px \rfloor\) 两部分后被放回去(当然 \(0<p<1\) 是肯定的),同时可重集内其它所有元素都会增加 \(q\)。一问 \(m\) 秒内每秒被提取的元素大小,二问 \(m\) 秒后可重集所有元素的大小。为了减少输出量,你只用每 \(t\) 个输出一个。
做法解析
\(k=0\) 的 \(O(m\log m)\) 显然。\(k>0\) 的 \(O(m\log m)\) 仍然很简单:你考虑“给除了切开的元素之外其它所有元素每次都加 \(k\)”其实就等价于“维护全局的偏移量,然后对于切开的元素减去一个 \(k\)”。堆维护即可。
然而数据范围需要我们做到 \(O(m)\)。听好接下来的思路——我们把元素分为三类:没有被切过的、被切成 \(\lfloor px\rfloor\) 的、被切成 \(x-\lfloor px\rfloor\) 的,对于这三类每个都开个队列来存放。我们一开始先给所有元素从大到小依次加入第一个队列,然后那么我们每次选出要切的一定属于这三个队列的队首。
- 为什么呢?(不妨令 \(x_1\ge x_2\))
- 对于第一类来说显然如此。
- 对于第二类:我们可以证明:\(\lfloor px_1\rfloor+k\ge\lfloor p(x_2+k)\rfloor\)。
- 对于第三类:我们可以证明:\(x_1-\lfloor px_1\rfloor+k\ge x_2+k-\lfloor p(x_2+k)\rfloor\)。
证明此略。
所以这么做就完了。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e5+5,nInf=-0x3f3f3f3f;
int N,M,K,U,V,T,A[MaxN];
queue<int> q[3];
int main(){
readis(N,M,K,U,V,T);
for(int i=1;i<=N;i++)readi(A[i]);
sort(A+1,A+N+1);for(int i=N;i>=1;i--)q[0].push(A[i]);
for(int i=1;i<=M;i++){
pii cp={nInf,0};
for(int j=0;j<3;j++)maxxer(cp,{q[j].size()?q[j].front():nInf,j});
auto [x,id]=cp;x+=(i-1)*K;q[id].pop();if(!(i%T))writip(x);
int na=1ll*x*U/V,nb=x-na;na-=i*K,nb-=i*K;
q[1].push(na),q[2].push(nb);
}
puts("");
for(int i=1;i<=N+M;i++){
pii cp={nInf,0};
for(int j=0;j<3;j++)maxxer(cp,{q[j].size()?q[j].front():nInf,j});
auto [x,id]=cp;q[id].pop();if(!(i%T))writip(x+K*M);
}
return 0;
}
浙公网安备 33010602011771号