bzoj4293: [PA2015]Siano

这题搞了我一晚上……因为某些傻X的问题……

窝这状态真是药丸TAT

这个题嘛……大家的题解都说线段树,时限也开了30s明摆着告诉你是nlogn……

不过我们发现a[i]<=10^6

那么我就有一个以空间换时间的做法,并且只需要用很小的空间就可以把复杂度降到O(n)(或者说是O(maxa[i]+n))

首先,这个被收割的稻草的A值显然有单调性,换句话说,每次收割都会有一个左端点

接着,我们发现,如果初始高度确定,生长时间确定,收割的门槛也确定,那么被收割的最低高度是可以算出来的

那么我们可以搞个单调栈,然后搞一搞就做完了

 

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define N 1000006

using namespace std;
inline ll read(){
	ll ret=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while ('0'<=ch&&ch<='9'){
		ret=ret*10-48+ch;
		ch=getchar();
	}
	return ret;
}
int n;
ll a[N],s[N];
ll d[N/2],b[N/2],day;
int stk[N/2],top;
int to[N/2];
const int m=1e6;

int main(){
	n=read();day=read();
	for (int i=1;i<=n;++i) ++a[read()];
	for (int i=1;i<=day;b[i++]=read()) d[i]=read();
	s[0]=a[0]=0;
	for (int i=1;i<=m;++i) s[i]=s[i-1]+(ll)a[i]*i;
	for (int i=1;i<=m;++i) a[i]+=a[i-1];
	to[0]=d[0]=b[0]=0;
	top=0;stk[top++]=0;
	for (int k=1;k<=day;++k){
		ll res=0;
		int last=m,x=max((ll)to[stk[top-1]],min((b[k]-b[stk[top-1]])/(d[k]-d[stk[top-1]]),(ll)m));
		for (;x<last;x=max((ll)to[stk[top-1]],min((b[k]-b[stk[top-1]])/(d[k]-d[stk[top-1]]),(ll)m))){
			res+=(d[k]-d[stk[top-1]])*(s[last]-s[x])+(b[stk[top-1]]-b[k])*(a[last]-a[x]);
			if ((last=x)>to[stk[top-1]]) break;
			if (!--top) break;
		}
		if ((to[k]=last)<m) stk[top++]=k;
		printf("%lld\n",res);
	}
	return 0;
}

  

你可能感兴趣的:(bzoj4293: [PA2015]Siano)