【洛谷2827】蚯蚓(单调队列+三路归并)

点此看题面

大致题意:(n)只长度为自然数(可能为(0))的蚯蚓,每一秒会将最长的蚯蚓切成两半:长度分别为 (lfloor px floor)(x-lfloor px floor)(长度为(0)的蚯蚓也会被保留),此外,除了刚产生的两只蚯蚓,其余蚯蚓的长度都会增加(q),现在要你求出(m)秒内每一秒被切断的蚯蚓在被切断前的长度和m秒后所有蚯蚓的长度(从大到小)。

题解

这道题目题意看起来比较复杂,其实我们可以这样考虑:
先将原始蚯蚓长度从大到小排序,每一次将砍断后蚯蚓较长的一段较短的一段分别存入两个数组中,由于被砍断的蚯蚓的原始长度是递减的,因此我们可以保证原始数组以及新建的两个数组满足不严格单调递减的性质,因此每次砍断的蚯蚓必然位于三个数组的队首,从而可以再接近于(O(1))的时间内确定接下来要砍断的蚯蚓。
最后,再将这三个有序的数组进行三路归并等价于两次二路归并),然后即可求出答案。

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define LL long long
#define N 100000
#define M 7000000
using namespace std;
LL n,m,q,u,v,t,a[N+5],b[M+5],c[M+5],s[N+M+5],s_[N+M+5]; 
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
	x=0;char ch;
	while(!isdigit(ch=tc()));
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
bool cmp(LL x,LL y)
{
	return x>y;
}
void merge(LL *X,LL *Y,int H1,int T1,int H2,int T2)//二路归并,将X[]与Y[]两个数组进行归并,并保证其有序
{
	int i=H1-1,j=H2-1,k=0;
	while(i<T1&&j<T2)
	{
		if(X[i+1]>Y[j+1]) s[++k]=X[++i];
		else s[++k]=Y[++j]; 
	}
	while(i<T1) s[++k]=X[++i];
	while(j<T2) s[++k]=Y[++j];
}
int main()
{
	register int i;
	for(read(n),read(m),read(q),read(u),read(v),read(t),i=1;i<=n;++i) read(a[i]);
	sort(a+1,a+n+1,cmp);//将原始数组a[]数组按从大到小的顺序排序
	int H1=1,H2=1,H3=1,T2=0,T3=0;//分别记录a[]数组的队首、b[]数组的队首和队尾以及c[]数组的队首与队尾
	LL tot=-q;//初始化增长的总长度
	for(i=1;i<=m;++i)
	{
		tot+=q;//更新增长的总长度
		LL Max=max(H1<=n?a[H1]:-tot-q,max(H2<=T2?b[H2]:-tot-q,H3<=T3?c[H3]:-tot-q))+tot;//求出最长的蚯蚓的长度
		if(!(i%t)) write(Max),putchar(' ');
		if(H1<=n&&!(Max^(a[H1]+tot))) ++H1;//判断该蚯蚓属于哪个数组
		else if(H2<=T2&&!(Max^(b[H2]+tot))) ++H2;
		else ++H3;
		LL s1=(1LL*Max*u)/v,s2=Max-s1;//砍断该蚯蚓
		b[++T2]=max(s1,s2)-tot-q,c[++T3]=min(s1,s2)-tot-q;//将蚯蚓分别存入b[]数组和c[]数组,并将其长度减去当前的增长长度,因为刚砍断的蚯蚓长度不会增加
	}
	putchar('
');
	merge(a,b,H1,n,H2,T2);//将a[],b[]数组归并
	for(i=1;i<=n+T2-H1-H2+2;++i) s_[i]=s[i];
	merge(s_,c,1,n+T2-H1-H2+2,H3,T3);将a[],b[]数组归并后得到的数组与c[]数组归并
	for(i=t;i<=n+m;i+=t) write(s[i]+tot+q),putchar(' ');
	return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2827.html