P2827 蚯蚓

看完题目以后,我马上想到了用优先队列,但是这里面m<=7*10^6,用优先队列会超时。
所以开始挖掘题目的隐藏条件。

经过观察发现,切蚯蚓都是在同一个地方切,切完以后分为了比例相同的两个小段。因为题目说每次切断最长的蚯蚓,那么我们就可以猜想先切掉的蚯蚓分成的两个小段分别比后切的蚯蚓分成的两个小段更长(当然也有可能一样长)。
证明:
设先切掉的蚯蚓分成了l1,r1,后切掉的分成了l2,r2
1.如果蚯蚓不会变长(也就是一起变长),那么证明肯定成立。(l1 >= l2,r1 >= r2)
2.回到原题,当切第一条蚯蚓的时候,第二条蚯蚓增加了q,而后面把q分给l2,r2;当切第二条蚯蚓的时候,l1,r1都增加q。很明显有q >= q * p(0 <= p <= 1)
证毕。
所以只需要再开两个队列,分别存储l和r
对于每秒都会增长的长度,只需要把增加的长度用一个数先存起来,使用的时候再加上即可
时间复杂度O(nlogn+m)
代码(有几个细节需要注意,最好自己打一遍)

#pragma GCC optimize("Ofast")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e5 + 10, M = 7e6 + 10;
int n, m, q, u, v, T;
int a[N], h1[M], h2[M], l, l1, r1, l2, r2;
double p;
bool cmp(int p1, int p2) { return p1 > p2; }
int main() {
	cin >> n >> m >> q >> u >> v >> T;
	p = double(u) / v;
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	sort (a + 1, a + n + 1, cmp);
	l = l1 = l2 = 1; r1 = r2 = 0;
	int now = 0;
	for (int i = 1; i <= m; i++) {
		int maxx, pos = -1;
		if (l <= n && pos == -1) maxx = a[l], pos = 0;//查找最大值 
		if (l1 <= r1 && (pos == -1 || maxx < h1[l1])) maxx = h1[l1], pos = 1;
		if (l2 <= r2 && (pos == -1 || maxx < h2[l2])) maxx = h2[l2], pos = 2;
		if (!pos) l++; else if(pos == 1) l1++; else l2++;
		maxx += now; now += q;//把maxx变成真实长度 
		int d1 = (int)((double)maxx * p), d2 = maxx - d1;
		h1[++r1] = d1 - now, h2[++r2] = d2 - now; //d1,d2都是真实的长度,存到队列里面要减去now 
		if (i % T == 0) printf("%d ", maxx); 
	}
	puts("");
	for (int i = 1; i <= n + m; i++) {
		int maxx, pos = -1;//把队列合并,类似归并排序
		if (l <= n && pos == -1) maxx = a[l], pos = 0;
		if (l1 <= r1 && (pos == -1 || maxx < h1[l1])) maxx = h1[l1], pos = 1;
		if (l2 <= r2 && (pos == -1 || maxx < h2[l2])) maxx = h2[l2], pos = 2;
		if (!pos) l++; else if(pos == 1) l1++; else l2++;
		if (i % T == 0) printf("%d ", maxx + now);
	}
	puts("");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值