您的位置:首页 > 其它

【NOIP2016A组模拟7.13】搬运干草捆

2016-07-14 15:57 357 查看

题目



题解

做模拟赛时我就想了出来,然而删除节点时没有更新father,于是GG……

首先可以列出一个DP的式子f(i,j)=min(f(i−1,k))+|j−hi|∀k≥j

于是我们设g(i,j)=min(f(i−1,k)),∀k≥j则f(i,j)=g(i,j)+|j−hi|

对于一个i来说,g(i)的图象即为:



那么|j−hi|的图象为:



两个相加得到f:



那么g(i+1)就变成了:



由上图我们可以看出,由原先的g(i),它加上两段一次函数(y=-x+k和y=x-k)得到一个新的f,而新的f,将其所有斜率小于0的去掉,就得到了新的g。

由是,我们需要一种数据结构,来支持找到分界点所在的区间,然后分两边分别加上不同的一次函数,于是我们可以用splay来解决这个,每个节点有三元组(t,k,b)表示一条射线y=kx+b,它的左端点的横坐标为t,首先我们找出当前分界点所在的区间然后将当前点分成两个,将伸展树分裂,分别加上不同的一次函数,然后合并,查询是否有斜率小于0的射线并删除,然后要增加一条斜率为0左端点为0的射线。

注意:删点时要改father啊啊啊

贴代码:

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

typedef long long LL;
typedef double db;

int get(){
char ch;
int s=0;
bool pd=0;
while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
if (ch=='-')pd=1;
else s=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
if (pd)return -s;
return s;
}

const int N = 100010;

int n;
struct point{
LL x,k,b,ak,ab;
int s[2];
}tree[N*2];
int fa[N*2],tot,root;
int q[N*2],k;

void down(int x){
if (tree[x].ak!=0||tree[x].ab!=0){
int ls=tree[x].s[0],rs=tree[x].s[1];
if (ls){
tree[ls].k+=tree[x].ak;
tree[ls].b+=tree[x].ab;
tree[ls].ak+=tree[x].ak;
tree[ls].ab+=tree[x].ab;
}
if (rs){
tree[rs].k+=tree[x].ak;
tree[rs].b+=tree[x].ab;
tree[rs].ak+=tree[x].ak;
tree[rs].ab+=tree[x].ab;
}
tree[x].ak=tree[x].ab=0;
}
}

void clear(int x){
k=0;
while(x){
q[++k]=x;
x=fa[x];
}
fd(i,k,1)down(q[i]);
}

bool pd(int x){
if (tree[fa[x]].s[0]==x)return 0;
return 1;
}

void rotate(int x){
int y=fa[x],z=fa[y];
int tx=pd(x),ty=pd(y);
if (z)tree[z].s[ty]=x;
fa[x]=z;
if (tree[x].s[tx^1])fa[tree[x].s[tx^1]]=y;
tree[y].s[tx]=tree[x].s[tx^1];
tree[x].s[tx^1]=y;
fa[y]=x;
}

void splay(int x){
clear(x);
while(fa[x]){
if (fa[fa[x]]){
if (pd(x)==pd(fa[x]))rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}

int pre(int now,int v){
if (!now)return 0;
if (tree[now].x<=v){
int ans=pre(tree[now].s[1],v);
if (ans)return ans;
return now;
}
return pre(tree[now].s[0],v);
}

int first(int now){
if (!tree[now].s[0])return now;
return kth(tree[now].s[0]);
}

void add(int now,int k,int b){
tree[now].ak+=k;
tree[now].ab+=b;
tree[now].k+=k;
tree[now].b+=b;
}

void clean(int &now){
if (!now)return;
down(now);
if (tree[now].k<0){
clean(tree[now].s[1]);
now=tree[now].s[1];
}
else{
clean(tree[now].s[0]);
fa[tree[now].s[0]]=now;
}
}

void split(int x,int sig){
fa[tree[x].s[sig]]=0;
tree[x].s[sig]=0;
}

int main(){
n=get();
tree[root=tot=1].x=1;
fo(i,1,n){
int x=get();
int u=pre(root,x),v=0;
if (tree[u].x==x){
if (x>1){
v=pre(root,x-1);
splay(u);
split(u,0);
splay(v);
}
}
else{
splay(u);
tree[v=++tot]=tree[u];
tree[v].x=x;
fa[tree[v].s[1]]=v;
tree[v].s[0]=tree[u].s[1]=0;
swap(u,v);
}
if (!v){
splay(u);
add(u,1,-x);
root=u;
}
else if (!u){
splay(v);
add(v,-1,x);
root=v;
u=v;
}
else{
add(v,-1,x);
add(u,1,-x);
splay(u);
splay(v);
tree[u].s[0]=v;
fa[v]=u;
}
clean(u);
int w=first(u);
splay(w);
if (tree[w].k==0)tree[w].x=1;
else
if (tree[w].x>1){
int r;
tree[r=++tot].x=1;
tree[r].b=tree[w].k*tree[w].x+tree[w].b;
tree[r].k=0;
tree[w].s[0]=r;
fa[r]=w;
}
root=w;
}
int w=first(root);
splay(w);
printf("%lld\n",tree[w].k+tree[w].b);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: