BZOJ3672:[Noi2014]购票 (斜率优化DP+二分+(树上CDQ分治/树链剖分))
来源:互联网 发布:wind python高频数据库 编辑:程序博客网 时间:2024/05/16 17:26
题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3672
题目分析:这题和NOI2007货币兑换Cash差不多,只不过它斜率优化的式子要简单些,并把原先的序列变成了树,还加上了距离限制。
我们先考虑一种比较暴力的做法:能更新某个点答案的那些点一定在它父亲到它祖先某个点u的连续一段上,于是不妨用倍增找出这个u,然后做一次树剖。在DFS序的线段树上用一个数组存它对应区间的点所构成的凸包,查找时二分即可。这题不需要套平衡树,因为并没有要求强制在线。时间复杂度
CODE(树剖):
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=200100;const int maxl=20;const long long oo=1e18;const double eps=1e-8;typedef long long LL;struct edge{ int obj; LL len; edge *Next;} e[maxn];edge *head[maxn];int cur=-1;int id[maxn<<2];int Le[maxn<<2];int Ri[maxn<<2];int que[maxn*maxl];int tail=0;int fa[maxn][maxl];int dep[maxn];LL dis[maxn];int Size[maxn];int Son[maxn];int Top[maxn];int bot[maxn];int dfsx[maxn];int Time=0;LL p[maxn];LL q[maxn];LL l[maxn];LL f[maxn];int n,t;void Add(int x,int y,LL z){ cur++; e[cur].obj=y; e[cur].len=z; e[cur].Next=head[x]; head[x]=e+cur;}void Dfs1(int node){ Size[node]=1; for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; fa[son][0]=node; dep[son]=dep[node]+1; dis[son]=dis[node]+p->len; Dfs1(son); Size[node]+=Size[son]; if (Size[son]>Size[ Son[node] ]) Son[node]=son; }}void Dfs2(int node){ dfsx[++Time]=node; bot[node]=Time; int son=Son[node]; if (son) { Top[son]=Top[node]; Dfs2(son); } for (edge *p=head[node]; p; p=p->Next) { son=p->obj; if (son!=Son[node]) { Top[son]=son; Dfs2(son); } }}int Jump(int x,LL y){ for (int j=maxl-1; j>=0; j--) { int z=fa[x][j]; LL w=dis[x]-dis[z]; if (y>=w) x=z,y-=w; } return x;}LL Binary(int L,int R,LL x){ while (L+1<R) { int mid=(L+R)>>1; int k=que[mid-1]; int j=que[mid]; if (f[j]-f[k]<x*(dis[j]-dis[k])) L=mid; else R=mid; } int k=que[L],j=que[R]; return min(f[j]-dis[j]*x,f[k]-dis[k]*x);}LL Query(int root,int L,int R,int x,int y,LL k){ if ( y<L || R<x ) return oo; if ( x<=L && R<=y ) return Binary(Le[root],Ri[root],k); int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; LL vl=Query(Left,L,mid,x,y,k); LL vr=Query(Right,mid+1,R,x,y,k); return min(vl,vr);}LL Work(int u,int v,LL x){ if (Top[u]==Top[v]) { if (bot[u]>bot[v]) swap(u,v); return Query(1,1,n,bot[u],bot[v],x); } if (dep[ Top[u] ]<dep[ Top[v] ]) swap(u,v); int w=Top[u]; LL val=Query(1,1,n,bot[w],bot[u],x); u=fa[w][0]; return min(val, Work(u,v,x) );}void Build(int root,int L,int R){ if (L==R) { int x=dfsx[L]; id[root]=Top[x]; Le[root]=++tail; que[tail]=x; Ri[root]=tail; if (L==1) return; int y=Jump(x,l[x]); f[x]=Work(fa[x][0],y,p[x])+dis[x]*p[x]+q[x]; return; } int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; Build(Left,L,mid); Build(Right,mid+1,R); if ( id[Left] && id[Left]==id[Right] ) id[root]=id[Left]; else return; Le[root]=tail+1; for (int i=L; i<=R; i++) { int x=dfsx[i]; que[++tail]=x; while ( Le[root]+2<=tail ) { int y=que[tail-1],z=que[tail-2]; double vl=(double)(f[x]-f[y])/(double)(dis[x]-dis[y]); double vr=(double)(f[y]-f[z])/(double)(dis[y]-dis[z]); if (vl-vr>eps) break; que[--tail]=x; } } Ri[root]=tail;}int main(){ freopen("3672.in","r",stdin); freopen("3672.out","w",stdout); scanf("%d%d",&n,&t); for (int i=1; i<=n; i++) head[i]=NULL; for (int i=2; i<=n; i++) { int f; LL s; scanf("%d%lld%lld%lld%lld",&f,&s,&p[i],&q[i],&l[i]); Add(f,i,s); } fa[1][0]=1; Dfs1(1); Top[1]=1; Dfs2(1); for (int j=1; j<maxl; j++) for (int i=1; i<=n; i++) fa[i][j]=fa[ fa[i][j-1] ][j-1]; Build(1,1,n); for (int i=2; i<=n; i++) printf("%lld\n",f[i]); return 0;}
还有一种用树上CDQ分治的更加巧妙的
树上CDQ分治:
这里的限制条件是:每个点仅受祖先影响,或者每个点仅受该点所在子树的影响。(两个条件全局仅成立一个)
先讨论每个点仅受祖先影响的情况。
考虑以某个点 x 为分界线,将树分为两半。(虽然有多个联通块)
将根 R 所在的块包括 x 分治处理。
处理 R 所在的块中的修改操作,对树的其余部分的影响。
注意,只有从 x 到 R 的路径上的点才会对树的其余部分有影响,因为只有这一些点在树的其余部分到根的路径上。
将树的其余部分分治处理。(每个联通块单独处理)
分治后的根为与 x 相连的点。(即分治的部分中深度最小的点)
如果是每个点仅受所在子树的影响的话,将分治的顺序调换即可。
考虑怎样选取分界点 x ,显然选择重心最优。
换句话说,树上的CDQ分治,就是以点分治的方式来进行CDQ分治。
那么在这题中,我们记dis[node]表示node到根的距离。每次选取重心root,先递归root父亲所在的连通块,算出连通块中每一个点的最优答案,然后再扫一遍其他连通块的所有点。假设node在其他连通块中,设
(话说我的点分治还是写得很不熟啊,而且
CODE(点分治):
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=200100;const double eps=1e-8;const long long oo=1e18;typedef long long LL;struct edge{ int obj; LL len; edge *Next;} e[maxn<<1];edge *head[maxn];int cur=-1;struct data{ LL val; int Node,P;} kscla[maxn];int num;int fa[maxn];LL dis[maxn];bool vis[maxn];int Size[maxn];int max_son[maxn];LL f[maxn];LL r[maxn];LL q[maxn];LL l[maxn];int sak[maxn];int tail;int n,t;void Add(int x,int y,LL z){ cur++; e[cur].obj=y; e[cur].len=z; e[cur].Next=head[x]; head[x]=e+cur;}void Dfs1(int node){ for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if (son!=fa[node]) { fa[son]=node; dis[son]=dis[node]+p->len; Dfs1(son); } }}void Dfs2(int node,int from){ sak[++tail]=node; max_son[node]=0; Size[node]=1; for (edge *p=head[node]; p; p=p->Next) { int to=p->obj; if ( to!=from && vis[to] ) { Dfs2(to,node); Size[node]+=Size[to]; if (Size[to]>Size[ max_son[node] ]) max_son[node]=to; } }}int Get(int node){ return max(Size[ max_son[node] ],tail-Size[node]);}void Dfs3(int node,LL x){ if (x+l[node]>=dis[node]) { num++; kscla[num].val=x+l[node]-dis[node]; kscla[num].Node=node; kscla[num].P=1; } for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if ( son!=fa[node] && vis[son] ) Dfs3(son,x); }}bool Comp(data x,data y){ return ( x.val<y.val || ( x.val==y.val && x.P<y.P ) );}LL Binary(LL x){ int L=1,R=tail; while (L+1<R) { int mid=(L+R)>>1; int y=sak[mid],z=sak[mid+1]; if (f[y]-dis[y]*x<f[z]-dis[z]*x) R=mid; else L=mid; } int y=sak[L],z=sak[R]; return min(f[y]-dis[y]*x,f[z]-dis[z]*x);}void Solve(int node){ tail=0; Dfs2(node,0); if (tail==1) { vis[node]=false; return; } int root=node; for (int i=2; i<=tail; i++) if ( Get(sak[i])<Get(root) ) root=sak[i]; node=fa[root]; while (vis[node]) node=fa[node]; vis[root]=false; if (vis[ fa[root] ]) Solve(fa[root]); int temp=fa[root]; while ( temp!=node && dis[root]-dis[temp]<=l[root] ) f[root]=min(f[root],f[temp]+(dis[root]-dis[temp])*r[root]+q[root]), temp=fa[temp]; temp=root,num=0; while (temp!=node) { num++; kscla[num].val=dis[root]-dis[temp]; kscla[num].Node=temp; kscla[num].P=0; temp=fa[temp]; } for (edge *p=head[root]; p; p=p->Next) { int son=p->obj; if ( son!=fa[root] && vis[son] ) Dfs3(son,dis[root]); } sort(kscla+1,kscla+num+1,Comp); tail=0; for (int i=1; i<=num; i++) if (kscla[i].P) { int x=kscla[i].Node; LL v=Binary(r[x]); f[x]=min(f[x],v+dis[x]*r[x]+q[x]); } else { int x=kscla[i].Node; sak[++tail]=x; while (tail>2) { int y=sak[tail-1],z=sak[tail-2]; double Left=(double)(f[y]-f[x])/(double)(dis[y]-dis[x]); double Right=(double)(f[z]-f[y])/(double)(dis[z]-dis[y]); if (Left-Right<-eps) break; sak[--tail]=x; } } for (edge *p=head[root]; p; p=p->Next) { int son=p->obj; if ( son!=fa[root] && vis[son] ) Solve(son); }}int main(){ freopen("ticket.in","r",stdin); freopen("ticket.out","w",stdout); scanf("%d%d",&n,&t); for (int i=1; i<=n; i++) head[i]=NULL,vis[i]=true; for (int i=2; i<=n; i++) { int F; LL s; scanf("%d%lld%lld%lld%lld",&F,&s,&r[i],&q[i],&l[i]); Add(i,F,s); Add(F,i,s); } Dfs1(1); for (int i=2; i<=n; i++) f[i]=oo; Solve(1); for (int i=2; i<=n; i++) printf("%lld\n",f[i]); return 0;}
- BZOJ3672:[Noi2014]购票 (斜率优化DP+二分+(树上CDQ分治/树链剖分))
- [BZOJ3672][NOI2014]购票-点分治-CDQ分治-斜率优化DP
- bzoj3672 [ NOI2014 ] -- 树上CDQ分治 + 斜率优化DP
- [BZOJ3672][NOI2014]购票 树分治斜率优化
- BZOJ3672: [Noi2014]购票 树分治 斜率优化
- 【NOI2014】【cdq点分治】【斜率优化】购票
- 树上CDQ分治 NOI2014购票
- bzoj 3672: [Noi2014]购票 树上cdq分治
- 【bzoj3672】[Noi2014]购票 斜率优化+树链剖分+线段树+凸包+三分
- BZOJ 3672 NOI2014 购票 树的点分治+斜率优化
- 【BZOJ 3672】[Noi2014]购票 树分治+斜率优化
- 【BZOJ 3672】[Noi2014]购票 树分治+斜率优化
- bzoj3672: [Noi2014]购票
- 【bzoj3672&&uoj7】[Noi2014]购票
- bzoj3672: [Noi2014]购票
- bzoj3672 [Noi2014]购票
- BZOJ3672: [Noi2014]购票
- [BZOJ3963][WF2011]MachineWorks(斜率优化dp+cdq分治)
- 基于linux vim环境python代码自动补全
- 条款6:阻止编译器自动生成拷贝构造函数和赋值函数
- MongoDB实战-分片集群实战
- Spring思维导图,让Spring不再难懂(mvc篇)
- 垃圾收集器与内存分配策略
- BZOJ3672:[Noi2014]购票 (斜率优化DP+二分+(树上CDQ分治/树链剖分))
- OCI接口简介及其在VC++中的应用(上)
- mybatis动态Sql(if-where)和sql片段
- CI框架集成微信APP支付
- 在VMware vSphere Client下使用Linux虚拟机安装CentOS6.4系统
- bzoj3454 家族(并查集)
- Vue 2.0 的建议学习顺序
- MySQL相关的函数
- Gradle's dependency cache may be corrupt解决方案