【bzoj2149】拆迁队

26 篇文章 0 订阅
6 篇文章 0 订阅

似乎又是一道cdq维护dp

这题首先要按照d[i]=a[i]-i的值分层dp。

在cdq分治中,相当于前一层dp为修改,当前层为询问。

考虑如何维护[l,mid]的修改对于[mid+1,r]的询问的影响。

列出dp方程发现只要寻找最小值即可。

然后就在维护的凸包上三分最小值的位置就好。

#include <bits/stdc++.h>
#define gc getchar()
#define N 120009
#define mid (l+r>>1)
#define ll long long
using namespace std;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll n,m,a[N],b[N],d[N],f[N],sta[N],top,g[N];
vector<ll> p[N];
struct node
{
	ll x,y,pos,k;
	node(ll pos=0,ll k=0):pos(pos),k(k)
	{
		x=d[pos],y=g[pos]-a[pos]*pos-a[pos]+(pos+1)*pos/2;
	}
	bool operator <(const node &rhs) const
	{
		return pos<rhs.pos;
	}
}q[N],now[N],st[N];
bool cmp(const node &lhs,const node &rhs)
{
	return d[lhs.pos]<d[rhs.pos]||(d[lhs.pos]==d[rhs.pos]&&!lhs.k);
}
ll read()
{
	ll x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	ll s=ch-'0';
	while (ch=gc,ch<='9'&&ch>='0') s=s*10+ch-'0';
	return s*x;
}
void solve(ll l,ll r)
{
	if (l==r)
	{
		if (q[l].k)
			g[q[l].pos]+=a[q[l].pos]+b[q[l].pos]+(q[l].pos-1)*q[l].pos/2;
		return;
	}
	solve(l,mid);
	ll num=0;
	for (ll i=l;i<=mid;i++) if (!q[i].k) now[++num]=q[i];//modify in [l,mid]
	for (ll i=mid+1;i<=r;i++) if (q[i].k) now[++num]=q[i];//qry in [mid+1,r]
	sort(now+1,now+num+1,cmp);//sort for d
	top=0;
	for (ll i=1;i<=num;i++)//d is increasing
	{
#define j now[i]
		if (!j.k)//slope optimization
		{
			while (top>=2&&1.0*(j.y-st[top].y)*(st[top].x-st[top-1].x)<1.0*(st[top].y-st[top-1].y)*(j.x-st[top].x))
				top--;
			st[++top]=j;
		}
		else
		{
			ll L=1,R=top,len,mid_l,mid_r;//divide into three parts for the best
			while (R-L>4)
			{
				len=(R-L)/3,mid_l=L+len,mid_r=R-len;
				if (st[mid_l].x*j.pos+st[mid_l].y>st[mid_r].x*j.pos+st[mid_r].y) L=mid_l;
				else R=mid_r;//min
			}
			while (L<=R) g[j.pos]=min(g[j.pos],st[L].x*j.pos+st[L].y),++L;
		}
#undef j
	}
	solve(mid+1,r);
}
int main()
{
	n=read();
	for (ll i=1;i<=n;i++) a[i]=read(),d[i]=a[i]-i,sta[i]=g[i]=inf;
	for (ll i=1;i<=n;i++) b[i]=read();
	for (ll i=1;i<=n;i++)
	{
		if (d[i]<0) continue;
		f[i]=upper_bound(sta+1,sta+n+1,d[i])-sta;
		p[f[i]].push_back(i);
		sta[f[i]]=min(sta[f[i]],d[i]);
	}
	p[0].push_back(0);
	for (ll i=1;;i++)
	{
		if (p[i].empty())
		{
			ll ret=inf;
			for (ll j=0,k;j<(ll)p[i-1].size();j++)
			{
				k=p[i-1][j];
				ret=min(ret,g[k]+(a[k]*2+n-k+1)*(n-k)/2);
			}
			printf("%lld %lld\n",i-1,ret);
			break;
		}
		m=0;
		for (ll j=0;j<(ll)p[i-1].size();j++) q[++m]=node(p[i-1][j],0);
		for (ll j=0;j<(ll)p[i].size();j++) q[++m]=node(p[i][j],1);
		sort(q+1,q+m+1);
		solve(1,m);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值