SPOJ - UNTITLE1 Untitled Problem II

目录

题目

传送门

解法

考虑一次操作 \((0,x,y,k)\) 对前缀和 \(s_i\) 的影响:

\[\Delta =\begin{cases} k\cdot(i-x+1), & x\le i\le y \\k\cdot (y-x+1), & i>y\end{cases} \]

先给每个下标设定一个 \(c_i\) 表示增加的定值,\(k_i\) 表示增加的 \(i\) 的系数。

那么询问 \((x,y)\) 的答案就是:

\[\text{Ans}=\max_{i=x}^y\{s_i+c_i+k_i\cdot i\} \]

显然是过不去的。不过事实上可以将区间分块。

  • \(s_i\) 还是表示 \(i\) 下标,处理非整块的情况。
  • \(c_i,k_i\) 都表示第 \(i\) 个块的值。

问题转化为在长度为 \(B\) 的块内查找上文的 \(\max\)。

将下标 \(i\) 抽象成点 \((i,s_i)\),那么 \(\text{Ans}\) 相当于斜率为 \(-k_{bl_i}\) 的过点 \(i,i\in S_{bl_i}\) 的最大截距。维护一个凸包,然后在凸包上二分斜率即可。当凸包被散块修改时,暴力重构凸包。

接下来计算一下复杂度:

  • 修改。\(\mathcal O(m\cdot (B+\frac{n}{B}))\)。
  • 查询。处理散块是 \(\mathcal O(mB)\) 的;对于整块,暴力重构单次是 \(\mathcal O(B)\) 的,总共是 \(\mathcal O(mB)\) 的,二分是 \(\mathcal O(m\cdot \frac{n}{B}\cdot \log\sqrt B)\) 的。

所以最优块长应该满足 \(\frac{B^2}{\log \sqrt B}=n\)(但是我没算常数),可能比 \(\sqrt n\) 略大,我直接取了 \(\sqrt n\)。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
} 

#include <cmath>
#include <iostream>
using namespace std;
typedef long long ll;

const int maxn=5e4+500,maxb=230;

int n,m,B,bl[maxn],K,x,y;
int st[maxb][maxb];
ll s[maxn],k[maxn],c[maxn];

void modify(int pos,int l=0,int r=0) {
	if(l and r) {
		st[pos][0]=0;
		ll tmp=1ll*K*(l-x+1);
		for(int i=l;i<=r;++i)
			s[i]+=tmp,tmp+=K;
	}
	else k[pos]+=K,c[pos]+=1ll*K*(1-x);
}

ll query(int pos,int l=0,int r=0) {
	if(l and r) {
		ll tmp=s[l]+k[pos]*l;
		for(int i=l+1;i<=r;++i)
			tmp=max(tmp,s[i]+k[pos]*i);
		return tmp+c[pos];
	}
	if(!st[pos][0]) {
		int *tp=&st[pos][0];
		for(int i=(pos-1)*B+1;i<=pos*B;++i) {
			while((*tp)>1 and 1ll*(s[i]-s[st[pos][*tp]])*(st[pos][*tp]-st[pos][*tp-1])>1ll*(s[st[pos][*tp]]-s[st[pos][*tp-1]])*(i-st[pos][*tp]))
				--(*tp);
			st[pos][++(*tp)]=i;
		}
	}
	l=1,r=st[pos][0]-1; int mid,a,b;
	while(l<=r) {
		mid=l+r>>1;
		a=st[pos][mid];
		b=st[pos][mid+1];
		if(s[b]-s[a]>-k[pos]*(b-a))
			l=mid+1;
		else r=mid-1;
	}
	return s[st[pos][l]]+c[pos]+k[pos]*st[pos][l];
}

signed main() {
	n=read(9); B=sqrt(n);
	for(int i=1;i<=n;++i)	
		s[i]=s[i-1]+read(9),
		bl[i]=(i-1)/B+1;
	m=read(9);
	int op; ll ans;
	while(m--) {
		op=read(9),x=read(9);
		y=read(9);
		if(!op) {
			K=read(9);
			if(bl[x]^bl[y]) {
				modify(bl[x],x,bl[x]*B);
				modify(bl[y],(bl[y]-1)*B+1,y);
				for(int i=bl[x]+1;i<bl[y];++i)	
					modify(i);
			}
			else modify(bl[x],x,y);
			K*=(y-x+1);
			for(int i=y+1;i<=bl[y]*B;++i)
				s[i]+=K;
			for(int i=bl[y]+1;i<=bl[n];++i)
				c[i]+=K;
		}
		else {
			ans=0;
			if(bl[x]^bl[y]) {
				ans=query(bl[x],x,bl[x]*B);
				ans=max(ans,query(bl[y],(bl[y]-1)*B+1,y));
				for(int i=bl[x]+1;i<bl[y];++i)	
					ans=max(ans,query(i));
			}
			else ans=query(bl[x],x,y);
			print(ans,'\n');
		}
	}
	return 0;
}
上一篇:/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory 报错解决


下一篇:Ubuntu16.04下安装编译gcc10.1.0