uoj 279: 数据分块鸡 动态规划+可持久化线段树

        一开始看到这道题目的时候以为是dp+线段树修改什么的来维护。

       但是n=50000好奇怪啊。

       看了题解才知道原来是决策单调性+暴力可持久化线段树求某一个区间的花费。

       刚好没有怎么写过dp决策单调性的优化,就写一发吧。

AC代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define M 2000005
using namespace std;

int n,m,trtot,fst[N],pnt[N],nxt[N],rt[N],ls[M],rs[M],q[N],lf[N]; ll f[N];
struct node{ int x; ll y,z; }val[M];
node operator +(node u,node v){ return (node){u.x+v.x,u.y+v.y,u.z+v.z}; }
node operator -(node u,node v){ return (node){u.x-v.x,u.y-v.y,u.z-v.z}; }
void ins(int l,int r,int x,int &y,int u,node v){
	y=++trtot; val[y]=val[x]+v;
	if (l==r) return;
	int mid=l+r>>1;
	if (u<=mid){ rs[y]=rs[x]; ins(l,mid,ls[x],ls[y],u,v); }
	else{ ls[y]=ls[x]; ins(mid+1,r,rs[x],rs[y],u,v); }
}
node getsum(int x,int y,int k){
	int l=1,r=n,mid; node ans=(node){0,0,0};
	if (!k || x>y) return ans;
	x=rt[x-1]; y=rt[y];
	while (l<r){
		mid=l+r>>1;
		if (k>mid){
			ans=ans+val[ls[y]]-val[ls[x]];
			l=mid+1; x=rs[x]; y=rs[y];
		} else{
			r=mid; x=ls[x]; y=ls[y];
		}
	}
	return ans+val[y]-val[x];
}
ll calc(int l,int r){
	ll ans=getsum(r,n,l).x;
	node u=val[rt[r]]-getsum(1,r,l-1);
	ans+=(ll)u.x*(r-l)+u.y-u.z;
	int mid=l+r>>1;
	u=getsum(l,mid,l-1);
	ans+=u.z-(ll)u.x*l;
	u=getsum(mid+1,r,l-1);
	ans+=(ll)u.x*r-u.z;
	u=getsum(r+1,n,mid);
	node v=getsum(r+1,n,r)-u; u=u-getsum(r+1,n,l);
	ans+=(ll)v.x*r-v.y+u.y-(ll)u.x*l;
	return f[l]+ans;
}
int main(){
	scanf("%d%d",&n,&m);
	int i,x,y;
	for (i=1; i<=m; i++){
		scanf("%d%d",&x,&y);
		pnt[i]=x; nxt[i]=fst[y]; fst[y]=i;
	}
	for (y=1; y<=n; y++){
		rt[y]=rt[y-1];
		for (i=fst[y]; i; i=nxt[i]){
			x=pnt[i];
			ins(1,n,rt[y],rt[y],x,(node){1,x,y});
		}
	}
	f[1]=0; q[1]=1; lf[1]=2;
	int head=1,tail=1;
	for (i=2; i<=n; i++){
		if (head<tail && lf[head+1]<=i) head++;
		f[i]=calc(q[head],i);
		if (i<n){
			for (; i<lf[tail] && calc(q[tail],lf[tail])>=calc(i,lf[tail]); tail--);
			int l=max(i,lf[tail])+1,r=n,mid;
			while (l<r){
				mid=l+r>>1;
				if (calc(q[tail],mid)<calc(i,mid)) l=mid+1; else r=mid;
			}
			if (l<=n && calc(q[tail],l)>=calc(i,l)){ q[++tail]=i; lf[tail]=l; }
		}
	}
	printf("%lld\n",f[n]);
	return 0;
}


by lych

2017.2.2

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值