NOIP2016 提高组 蚯蚓

NOIP2016 提高组 蚯蚓

算法一

容易想到用优先队列维护最大值,但是有 “其余蚯蚓长度增加 \(q\)” 这个条件,考虑怎么快速地处理。我们把增加的总长度记为偏移量 \(delta\)。每个数在加入前,把不产生贡献的时间的偏移量减去,再存进去就可以了。时间复杂度 \(O(mlogn)\),用 priority_queue 会被卡常,换成 heap 能卡过去。

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
using namespace std;
using ll = long long;
const int LEN = 5e7;
char buf[100], *p1 = buf, *p2 = buf, obuf[LEN], *o = obuf;
inline int gc(){
	return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 100, stdin), p1 == p2) ? EOF : *p1++;
}
inline int rd(){
	int x = 0; char ch; bool f = 1;
	while(!isdigit(ch = gc())) f ^= (ch == '-');
	do x = (x << 3) + (x << 1) + (ch ^ 48); while(isdigit(ch = gc()));
	return f ? x : -x;
}
inline void output(int x){
	if(x < 0){
		x = ~x + 1;
		*o++='-';
	}
	if(x > 9) output(x / 10);
	*o++=(x % 10 + 48);
}
inline void write(int x, char c){
	output(x);
	*o++=c;
}
const int N = 1e5 + 5, M = 7e6 + 5;
int n, m, k, opt, cnt = 0; ll u, v;
int q[N + M];
signed main(){
//	freopen("earthworm.in","r",stdin);
//	freopen("earthworm.out","w",stdout);
	n = rd(), m = rd(), k = rd(), u = rd(), v = rd(), opt = rd();
	F(i, 1, n) q[++ cnt] = rd() - k;
	make_heap(q + 1, q + cnt + 1);
	F(t, 1, m){
		pop_heap(q + 1, q + cnt + 1);
		int ret = q[cnt] + t * k, x = ret * u / v, y = ret - x;
		if(t % opt == 0) write(ret, ' ');
		q[cnt] = x - (t + 1) * k;
		push_heap(q + 1, q + cnt + 1);
		q[++ cnt] = y - (t + 1) * k;
		push_heap(q + 1, q + cnt + 1);
	}
	*o++='\n';
	sort(q + 1, q + cnt + 1, [&](const int &i, const int &j){return i > j;});
	F(i, 1, cnt) if(!(i % opt)) write(q[i] + (m + 1) * k, ' ');
	fwrite(obuf, o - obuf, 1, stdout);
	return fflush(0), 0;
}

算法二

对于每次的最大值 \(x\),容易发现 \(x\)\(\lfloor px \rfloor\) 都具有单调性。想一下 \(x - \lfloor px \rfloor\) 有没有单调性。

先不考虑取整符号,可以化简为 \((1 - p)x\),考虑小数部分的话,这是具有单调性的。

加上取整符号呢?发现 $ x - \lfloor px \rfloor = \lceil (1 - p)x \rceil$,相当于会把一些数向右移动一点点变成最近的整数,容易理解这仍然具有不严格单调性。

所以三种值都具有不严格单减性

所以我们直接开三个队列分别维护 \(x, \lfloor px \rfloor, x - \lfloor px \rfloor\),每次取出的最大值就是三个队首中的最大值。模拟找数删数的过程就做完了。

时间复杂度 \(O(nlogn + m)\)

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
#define mk make_pair
#define pii pair<int, int>
using namespace std;
using ll = long long;
char buf[100], *p1 = buf, *p2 = buf;
inline int gc(){
	return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 100, stdin), p1 == p2) ? EOF : *p1++;
}
inline int rd(){
	int x = 0; char ch; bool f = 1;
	while(!isdigit(ch = gc())) f ^= (ch == '-');
	do x = (x << 3) + (x << 1) + (ch ^ 48); while(isdigit(ch = gc()));
	return f ? x : -x;
}
const int N = 1e5 + 5;
const int inf = 2e9;
int n, m, k, t, delta = 0;
ll u, v;
int a[N];
queue<int> q[3];
signed main(){
	n = rd(), m = rd(), k = rd(), u = rd(), v = rd(), t = rd();
	F(i, 1, n) a[i] = rd();
	sort(a + 1, a + n + 1, [&](const int &i, const int &j){return i > j;});
	F(i, 1, n) q[0].push(a[i]);
	F(i, 1, m){
		pii mx = max({ mk(q[0].empty() ? -inf : q[0].front(), 0), mk(q[1].empty() ? -inf : q[1].front(), 1), mk(q[2].empty() ? -inf : q[2].front(), 2)});
		q[mx.second].pop(), mx.first += delta, delta += k;
		q[1].push(mx.first * u / v - delta), q[2].push(mx.first - mx.first * u / v - delta);
		if(!(i % t)) printf("%d ", mx.first);
	} puts("");
	int cnt = 0;
	while(q[0].size() || q[1].size() || q[2].size()){
		pii mx = max({ mk(q[0].empty() ? -inf : q[0].front(), 0), mk(q[1].empty() ? -inf : q[1].front(), 1), mk(q[2].empty() ? -inf : q[2].front(), 2)});
		q[mx.second].pop();		
		if(!((++ cnt) % t)) printf("%d ", mx.first + delta);
	} return fflush(0), 0;
}

总结

本题中 \(m\) 过大的范围提示我们需要寻求一种更快速的线性维护方法,显然这个方法一定没有普适性,所以我们应该大胆猜想题目的情景有一些好用的性质,比如单调性、二分性等等。

posted @ 2024-11-20 17:04  superl61  阅读(3)  评论(0编辑  收藏  举报