【noi.ac #2126】ichi

题目

这里一个网格状的房间,一共有nn列,但是有正无穷的行。

小AA一开始在第00行,可以任意选择所在的列。现在他每次可以向下走一格(行号+1),或者向右下走一格(行列同时+1)。

在行走过程中,小AA需要不断消耗体力。具体来说,每经过一个第ii列的格子,需要消耗AiAi的体力。

现在小AA会一共行走mm次,每次会给出目标坐标第xx行第yy列,请你求出走到那所消耗的最小体力。

输入格式
第一行一共两个整数,nn和mm。

第二行一共nn个整数,AiAi。

接下来mm行,每行两个整数,xx和yy。

输出格式
mm行,每行一个整数,表示每个询问的最小体力。

样例
Input
6 4
2 2 3 4 3 4
4 5
3 4
3 4
2 3
Output
12
9
9
5
数据规模与约定
对于20%20%的数据,满足n,m≤100n,m≤100;

对于60%60%的数据,满足n,m≤1000n,m≤1000;

对于100%100%的数据,满足n,m≤105,1≤x≤y≤n,0≤Ai≤10000n,m≤105,1≤x≤y≤n,0≤Ai≤10000;

时间限制:1s

空间限制:512MB

思路

考虑暴力,可以发现我们消耗的能量是我们选择的区间和+(x-y+i)*min(ai),即sumy-sumi+(x-(y-i))*ai=(x-y)*ai+(i *ai-sumi)+sumy
发现这可以用单调队列维护
线段树每个区间维护一个单调队列。

代码

#include
#define ll long long
using namespace std;
const int N=1e5+77;
int n,q,X,ans,aa[N],sum[N],K[N],B[N],Q[N*30],c[N],bot,a[N];
struct Node
{
	int l,r,st,en;
}T[N<<2];
bool cmp(int x,int y)
{
	return K[x]>K[y]||(K[x]==K[y]&&B[x]<B[y]);
}
double pos(int x,int y) 
{
	return (double)(B[y]-B[x]) / (K[x]-K[y]); 
}
void baoli()
{
	scanf("%d%d",&n,&q);
	for(int i=1; i<=n; i++) scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
	while(q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ll aii=0x3f3f3f3f,yjy=0x3f3f3f3f;
		for(int i=y; i>=max(y-x,1); i--)
		{
			aii=min(aii,1ll*a[i]); 
			yjy=min(yjy,aii*(x-y+i-1)+sum[y]-sum[i-1]);
		}
		printf("%lld\n",yjy);
	}
}
void build(int i,int l,int r)
{
	T[i].l=l;
	T[i].r=r;
	int t=0,now=T[i].st=bot+1;
	for(int j=l; j<=r; j++)
		c[++t]=j;
	sort(c+1,c+t+1,cmp);
	Q[++bot]=c[1];
	for(int j=2; j<=t; j++)
		if(K[c[j]]<K[c[j-1]])
		{
			while(bot>now&&pos(Q[bot-1],Q[bot])>pos(Q[bot],c[j]))
				bot--;
			Q[++bot]=c[j];
		}
	T[i].en=bot;
	if(l==r)
		return;
	int M=l+r >> 1;
	build(i<<1,l,M);
	build(i<<1|1,M+1,r);
}

void query(int l,int r)
{
	while(l<r)
	{
		int M=(l+r >> 1)+1;
		if(X >= pos(Q[M-1],Q[M]))
			l=M;
		else
			r=M-1; 
	}
	ans=min(ans,K[Q[l]]*X+B[Q[l]]);
}

void query(int i,int l,int r)
{
	if(l<=T[i].l&&T[i].r<=r)
	{
		query(T[i].st,T[i].en);
		return;
	}
	int M=T[i].l+T[i].r >> 1;
	if(l<=M)
		query(i<<1,l,r);
	if(r>M)
		query(i<<1|1,l,r);
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1; i<=n; i++)
		scanf("%d",&aa[i]),sum[i]=sum[i-1]+aa[i],K[i]=aa[i],B[i]=aa[i]*i-sum[i];
	build(1,1,n);
	while(q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ans=0x3f3f3f3f;
		X=x-y;
		query(1,y-x+1,y);
		printf("%d\n",ans+sum[y]);
	}
}

你可能感兴趣的:(【noi.ac #2126】ichi)