您的位置:首页 > 编程语言 > C语言/C++

NOIP2016Day2T2蚯蚓解题报告

2017-09-02 12:22 204 查看
原题目见洛谷(https://www.luogu.org/problem/show?pid=2827

其实这道题的数据特点写得非常详细个人认为这道题即使是纯暴力也可以得到25分。下面我来分分数段来讲解这道题:

【25分】

首先我们来看数据范围在第1.2.3个点我们发现数据m=0那么直接按题目方式输出就好了,第4.6个点n=1也直接暴力模拟就好了,具体代码不写了……

【65-85分】

我们最先想到的是用堆来维护所有蚯蚓的长度,如何来操作呢:我们每次从堆中取出最长的蚯蚓,将他切割后放入堆中。

那么就产生了一个问题:我们每次将蚯蚓取出后如何使得其他蚯蚓的长度增加q?

我们可以这么来做,用一个Add来记录当前每条蚯蚓应当增加的长度当我们的时间已经到达m时说明所有切割结束,那么理论上每一条蚯蚓都增加了Add=q*m的长度。但是当我们取出一条需要切割的蚯蚓时,这条蚯蚓的长度是不允许增加的,我们如何来操作呢?我们在切割后蚯蚓的长度上减去q在压入堆中就可以了,我们可以采用手打堆和快速输入输出来让程序跑得更快。用STL模板中的priority_queue时会T掉一些点,我在这里采用手打堆和输入输出优化分数为85分,此时算法时间复杂度约为O((n+m)log(n+m))代码如下:

#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;
(i % t == 0) put(Q.g[1] + Add), putchar(' ');
Q.Pop();
}
}


【100分】

现在我们改进一下,我们这样

想,如果我们将原来的蚯蚓设为l1,分割后的两条蚯蚓分别设为l2,l3,那么一定有l2<=l1,l3<=l1显然成立那么我们可以定义三个普通队列分别表示未切割过的的蚯蚓q1,切割过的左半段q2,切割过的有半段q3,输入完数据时将q1从大到小排序,每次需要切割时取出三个队列中队首的最大值切割就好之后将切割后的左右段分别压入q2,q3的队尾其他处理方式和用堆处理方式相同,于是算法时间复杂度为O(n+m),显然要快很多,总共用时1528ms

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long ll;
const i
4000
nt 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ NOIP