洛谷 P2827 [NOIP2016]蚯蚓

二话不说直接开干

首先,这个数据范围不小,如果使用堆或优先队列对所有的蚯蚓进行暴力维护的话将会导致 O ( m log ⁡ ( n + m ) ) O(m\log(n+m)) O(mlog(n+m))的复杂度——这个常不好卡。
所以我们想办法找到线性复杂度的做法。
观察每次剪断后的蚯蚓,假设 a , b a,b a,b是相继两段要被剪断的蚯蚓
a a a被剪成 a 1 = a p a_1=ap a1=ap a 2 = a ( 1 − p ) a_2=a(1-p) a2=a(1p)
b b b被剪成 b 1 = ( b + q ) p b_1=(b+q)p b1=(b+q)p b 2 = ( b + q ) ( 1 − p ) b_2=(b+q)(1-p) b2=(b+q)(1p)
假设 p > 1 2 p>\frac12 p>21,观察 a 1 , b 1 a_1,b_1 a1,b1 a 2 , b 2 a_2,b_2 a2,b2大小关系:
b 1 , b 2 b_1,b_2 b1,b2入队时, a 1 , a 2 a_1,a_2 a1,a2已经长长了 q q q
a 1 = a p + q , a 2 = a ( 1 − p ) + q a_1=ap+q,a_2=a(1-p)+q a1=ap+q,a2=a(1p)+q
b 1 = b p + p q , b 2 = b ( 1 − p ) + q − p q b_1=bp+pq,b_2=b(1-p)+q-pq b1=bp+pq,b2=b(1p)+qpq
对于 a 1 , b 1 a_1,b_1 a1,b1,显然 a p > b p , q > p q ap>bp,q>pq ap>bp,q>pq,故 a 1 > b 1 a_1>b_1 a1>b1
对于 a 2 , b 2 a_2,b_2 a2,b2,同时减去 q q q a > b , − p q < 0 a>b,-pq<0 a>b,pq<0,故 a 2 > b 2 a_2>b_2 a2>b2
所以,其实所有剪短之后的蚯蚓都是按照长短顺序单调入队的!
接下来的做法就不难了:
开三个队列 A , B , C A,B,C A,B,C,分别为原蚯蚓、剪断后 a p ap ap部分的蚯蚓和剪断后 a ( 1 − p ) a(1-p) a(1p)的蚯蚓
先给 A A A排个序,然后每次从三个队列中挑一条最长的剪。
为了表现蚯蚓的生长,用一个变量 d e l t a ( Δ ) delta(\Delta) delta(Δ)记录自一开始以来生长的长度
假设有两条蚯蚓 x , y x,y x,y x x x在队列中, y y y是刚刚产生的,而且原始状态下 x = y x=y x=y
x ′ x' x x x x在队列中储存的长度,其实际长度 x x x x ′ + Δ x'+\Delta x+Δ
在这一秒内, y y y出生了,然而 x x x已经生长,于是我们先让 Δ \Delta Δ增加为 Δ 2 \Delta_2 Δ2,再使得 y ′ = y − Δ 2 y'=y-\Delta_2 y=yΔ2
那么,现在再取出 x x x的话,得到的结果将是 x ′ + Δ 2 x'+\Delta_2 x+Δ2,即 x + p x+p x+p,而 y y y取出时结果仍然是 y y y
生长就完成了

#include<cstdio>
//#include<queue>
#include<algorithm>
#include<functional>
using namespace std;
int a[110000];long long delta;
template<typename Tp>
struct queue//超奥义玄学手打队列
{
	int head,tail;
	/*/For debugging
	
	Tp list[110];
	queue():head(1),tail(0){}
	
	/*///For judging
	
	Tp *list;
	queue():head(1),tail(0),list(new Tp[11110000]){}
	
	//*/
	int size(){return tail-head+1;}
	int front(){return list[head];}
	void pop(){++head;}
	void push(Tp x){list[++tail]=x;}
};
queue<long long>qa,qb,qc;
int getmax()
{
	long long maxa=-1,maxb=-1,maxc=-1;
	if(qa.size())maxa=qa.front()+delta;
	if(qb.size())maxb=qb.front()+delta;
	if(qc.size())maxc=qc.front()+delta;
	if(maxa>=maxb&&maxa>=maxc)
	/*
	这地方一开始打了大于号,于是挂了好久
	如果打了大于号的话会优先用c……然后两个-1就给我炸了
	*/
	{
		qa.pop();
		return maxa;
	}
	if(maxb>=maxa&&maxb>=maxc)//
	{
		qb.pop();
		return maxb;
	}
	qc.pop();
	return maxc;
}
int main()
{
	int n,m,q,u,v,t;
	scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	sort(a+1,a+n+1,greater<int>());
	for(int i=1;i<=n;i++)
		qa.push(a[i]);
	for(int i=1;i<=m;i++)
	{
		long long now=getmax();
		if(i%t==0)printf("%lld ",now);
		long long nowb=now*u/v,nowc=now-nowb;
		if(nowb<nowc)swap(nowb,nowc);
		delta+=q;
		qb.push(nowb-delta);
		qc.push(nowc-delta);
	}
	putchar(10);
	for(int i=1;i<=n+m;i++)
	{
		long long now=getmax();
		if(i%t==0)
			printf("%lld ",now);
	}
	putchar(10);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值