【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; }
相关文章推荐
- LinkedList基本用法
- 项目:个人主页=>用户头像(以及头像裁剪)+用户信息(点点点,查看更多)+滚动加载
- Intent 各种跳转
- Ubuntu会怎么玩物联网平台
- 剑指offer-5-面试31:连续子数组的最大和(时间效率)
- httpd.conf服务器配置简单介绍
- 静态路由的配置方法
- Linux中查看CPU信息【转】
- windows安装rsync
- centos python 安装 readability
- 利用双缓冲队列来减少锁的竞争
- JVM的GC日志分析
- CNN的训练图像与测试图像不一致的多尺度问题
- javascript prototype原型继承
- 如何通过Haystack建立pdf文档的全文索引
- 基于 Selenium WebDriver 的 Web 应用自动化测试
- HasgMap和Map的遍历方式
- 手机QQ内置网页,微信内置网页中进行分享到QQ和微信的操作
- Websocket协议的学习、调研和实现
- PhpStorm中如何使用FTP功能 详细操作方法