Description
本题中,我们将用符号
⌊c⌋
表示对
c
向下取整,例如:
Input
第一行包含六个整数
n,m,q,u,v,t
,其中:
n,m,q
的意义见问题描述;
u,v,t
均为正整数;你需要自己计算
p=u/v
(保证
0<u<v
)t是输出参数,其含义将会在输出格式中解释。
第二行包含
n
个非负整数,为
同一行中相邻的两个数之间,恰好用一个空格隔开。
保证
Output
第一行输出
⌊m/t⌋
个整数,按时间顺序,依次输出第
t
秒,第
第二行输出
⌊(n+m)/t⌋
个整数,输出
m
秒后蚯蚓的长度;需要按从大到小的顺序
依次输出排名第
同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要输出,你也应输出一个空行。
请阅读样例来更好地理解这个格式。
Sample Input
3 7 1 1 3 1
3 3 2
Sample Output
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2
Data Range
Problem Address
Solution
【60-85pts】堆
- 很容易想到用堆来维护所有蚯蚓的长度:
- 针对第一问,我们每次取出最长的那只蚯蚓,将其切割后再次插入堆中
- 针对第二问,我们直接将堆中所有元素按大小顺序取出
- 但这样还有一个问题:如何处理每次取出后,其余蚯蚓的长度都增加 q ?
- 我们另外记一个变量
Add ,表示当前每条蚯蚓(不考虑被切割的)的长度已经增加了多少,每次取出的时候加上 Add 就表示现在的实际长度(当然,插入堆中时还要再减回去) - 考虑到切割后的蚯蚓不能增加长度,我们把问题转化:直接将这切割出的两只蚯蚓长度都减去 q 再插入堆,就解决了这个问题
- 这样的总复杂度为
O((n+m)log(n+m)) ,应该是能通过大部分数据点的
Code-1
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e5 + 5, M = 7e6 + 5;
int n, m, q, u, v, t, Add;
double p;
struct BigRt
{
int g[N + M], l;
inline void Pop()
{
g[1] = g[l--];
int now = 1, nxt = 2, res = g[1];
while (nxt <= l)
{
if (nxt < l && g[nxt | 1] > g[nxt]) nxt |= 1;
if (res < g[nxt])
g[now] = g[nxt], nxt = (now = nxt) << 1;
else break;
}
g[now] = res;
}
inline void Push(const int &res)
{
g[++l] = res;
int now = l, nxt = l >> 1;
while (nxt)
{
if (res > g[nxt])
g[now] = g[nxt], nxt = (now = nxt) >> 1;
else break;
}
g[now] = res;
}
}Q;
inline int get()
{
char ch; int res = 0; bool f = true;
while (((ch = getchar()) < '0' || ch > '9') && ch != '-');
if (ch == '-') f = false;
else res = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
res = (res << 3) + (res << 1) + ch - '0';
return f? res : -res;
}
inline void put(int x)
{
if (x < 0)
x = -x, putchar('-');
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}
inline bool cmp(const int &x, const int &y) {return x > y;}
int main()
{
n = get(); m = get(); q = get();
u = get(); v = get(); t = get();
p = (double)u / v; Q.l = 0;
for (int i = 1; i <= n; ++i) Q.Push(get());
for (int i = 1; i <= m; ++i)
{
int x = Q.g[1] + Add; Q.Pop();
if (i % t == 0) put(x), putchar(' ');
int l = (int)(p * x), r = x - l;
Q.Push(l - Add - q); Q.Push(r - Add - q);
Add += q;
}
putchar('\n');
for (int i = 1; i <= n + m; ++i)
{
if (i % t == 0) put(Q.g[1] + Add), putchar(' ');
Q.Pop();
}
}
【100pts】队列
- 我们会发现蚯蚓的切割具有单调性:一只蚯蚓切割后会分为 ⌊px⌋ 和 x−⌊px⌋ 两个部分,对于其中的任意一个部分,在某一时刻切割出的那只蚯蚓必然会比在它之后切割出来的蚯蚓要长
- 我们用反证法对此予以证明:
- 设某一时刻被选出的某只蚯蚓切割前的长度为
ai
,经过
N
秒后,假设存在一只之前未被切割过的蚯蚓这一秒切割完后长度最大,我们记其
N 秒前的长度为 aj ,那么 ai,aj 必然要满足(我们先只考虑切割出的 ⌊px⌋ 那部分蚯蚓, x−⌊px⌋ 同理):ai×p+N×q≤(aj+N×q)×pai×p+N×q≤aj×p+N×q×p - 又因为
N
秒前长度为
ai 的蚯蚓被选出,所以那一时刻满足 ai≥aj ,而 p 的取值范围为0<p<1 ,所以必然满足ai×p+N×q>aj×p+N×q×p - 与之前的假设矛盾,因此上述情况不可能存在,我们证得蚯蚓的切割具有单调性
- 考虑记录三个队列,分别存储未切割过的蚯蚓和切割成的两只蚯蚓,每次将蚯蚓插入对应的队尾。根据我们上面推论得出的单调性,每次取出三个队头的最大值即可,蚯蚓长度的增加和上述堆做法的处理方式相同,这样的总复杂为 O(n+m)
Code-2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int Maxn = 2147483647;
const int N = 1e5 + 5, M = 7e6 + 5;
int n, m, q, u, v, t, Add;
int Q[3][M], qt[3], qw[3];
inline int get()
{
char ch; int res;
while ((ch = getchar()) < '0' || ch > '9');
res = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
res = (res << 3) + (res << 1) + ch - '0';
return res;
}
inline void put(int x)
{
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}
inline bool cmp(const int &x, const int &y) {return x > y;}
inline int GetMax()
{
int res = -Maxn, k;
for (int i = 0; i < 3; ++i)
if (qt[i] < qw[i] && res < Q[i][qt[i] + 1])
res = Q[i][qt[i] + 1], k = i;
qt[k]++; return res;
}
int main()
{
n = get(); m = get(); q = get();
u = get(); v = get(); t = get();
for (int i = 1; i <= n; ++i) Q[0][++qw[0]] = get();
sort(Q[0] + 1, Q[0] + qw[0] + 1, cmp);
for (int i = 1; i <= m; ++i)
{
int x = GetMax() + Add;
if (i % t == 0) put(x), putchar(i + t > m ? '\n' : ' ');
int l = (ll)x * u / v, r = x - l;
Q[1][++qw[1]] = l - Add - q;
Q[2][++qw[2]] = r - Add - q; Add += q;
}
if (t > m) putchar('\n');
int tmp = n + m;
for (int i = 1; i <= tmp; ++i)
{
int x = GetMax() + Add;
if (i % t == 0) {put(x); if (i + t <= tmp) putchar(' ');}
}
return 0;
}
Notice
- 在 BZOJ 上提交注意输出格式:行末无空格,第二行无回车
- 在 UOJ 上提交注意精度问题:计算切断后蚯蚓长度直接 ×u/v ,过程中可能会爆 int ,应强转 long_long