AcWing 133. 蚯蚓
思考
朴素版的暴力写法就是全部遍历,每次操作后再排序。
而聪明一点的人就用单调队列,但是m的范围是七百万,这是非用线性算法不可(堆每次维护是\(O(n log n)\)。
但是突破口在哪里?其实你用优先队列的时候已经想到了。
既然题目说除了切开的蚯蚓,其他蚯蚓每过一秒长q的长度,那么正常人是不会每操作一次遍历一遍堆的,一定是把所有累加的q(总和是\(i * q\))放在外面,等到要用的时候再加上。
这就是保留偏移量的方法,因为\(x_1 > x_2,那么一定存在 x_1 - delta > x_2 - delta\)。
推导
只能从优化数据结构入手了。但是不挖掘性质的话,最优解法至此结束。
我们先看切开蚯蚓后的情况,先从简单的入手,再一步一步推进(正解往往从猜测中出现)。
下文的所有情况,都是建立在正确合法的顺序之上。
假设现在有一只长度为\(x_1\)的蚯蚓要被切开,还有一只\(x_2\)的蚯蚓在下一秒要被切开。
那么必定有\(x_1 >= x_2\)。
那么第一秒的时候,切开的两段蚯蚓和x2的长度分别是:
第二秒的时候,x1切开的两段蚯蚓和x2切开的两段蚯蚓长度分别是:
我们会发现,1和3式很像,2和4式很像。
那么我们就拿切开的蚯蚓的左半边和左半边比较,右半边亦是如此,发现先切开的蚯蚓的同半边一定比后切开的蚯蚓的同半边要大,还未切开的蚯蚓显然也是如此。
如果时间延伸成:一只长度为\(x_1\)的蚯蚓要被切开,还有一只\(x_2\)的蚯蚓在下k秒被切开,那么规律也是一样的。
最有力的性质被找到了,也就是3种蚯蚓呈单调递减,那么就意味着每次操作之后省去了排序的时间,时间复杂度\(O(m)\)。
这个活交给队列来干,队列先进先出恰好符合。
但是有一点:三种蚯蚓之间没有明确的大小关系。那么只需要每次操作的时候取出三个队头,比较其大小再使用。
最后一点:十年OI一场空,不开long long见祖宗
100 pts
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
typedef long long ll;
ll n, m, q, u, v, t;
ll a[N];
queue<ll> q1, q2, q3;
int get(int dt)
{
ll a = -1, b = -1, c = -1;
if(q1.size()) a = q1.front() + q * dt;
if(q2.size()) b = q2.front() + q * dt;
if(q3.size()) c = q3.front() + q * dt;
ll res = max(a, max(b, c));
if(res == a) q1.pop();
else if(res == b) q2.pop();
else if(res == c) q3.pop();
return res;
}
int main()
{
cin >> n >> m >> q >> u >> v >> t;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
sort(a + 1, a + n + 1), reverse(a + 1, a + n + 1);
for(int i = 1; i <= n; i ++ ) q1.push(a[i]);
for(int i = 1; i <= m; i ++ )
{
ll x = get(i - 1);
if(i % t == 0) cout << x << ' ';
ll a = x * u / v; ll b = x - a;
q2.push(a - i * q);
q3.push(b - i * q);
}
cout << endl;
for(int i = 1; i <= n + m; i ++ )
{
ll x = get(m);
if(i % t == 0) cout << x << ' ';
}
return 0;
}
本文来自博客园,作者:{三季野花},转载请注明原文链接:https://www.cnblogs.com/SanGarden/articles/17072679.html