【bzoj3672】[Noi2014]购票 斜率优化+树链剖分+线段树+凸包+三分
2016-03-23 10:50
441 查看
f[i]表示从根到点i的最少票价
f[i]=min{f[j]+(dep[i]-dep[j])*p[i]+q[i] } (dep[i]-dep[j]<=li)
=f[j]-dep[j]*p[i]+dep[i]*p[i]+q[i]
f[j]=dep[j]*p[i]+f[i]-dep[i]*p[i]-q[i]
f[i]-dep[i]*p[i]-q[i]表示过点(dep[j],f[j])的斜率为p[i]的直线在y轴上的截距
因为p[i]>=0,所以答案一定在下凸壳上
pre[i]表示i最多能延伸到的祖先,这个可以二分什么的乱搞出来
求f[i]就是在fa[i]到pre[i]之间形成的凸壳上三分
树链剖分+线段树维护凸壳
线段树的每个节点暴力建出凸壳,复杂度O(nlog^2n)
每次查询按照剖分查就可以了,复杂度O(log^3n)
注意,本题算叉积的时候会爆longlong,要转成double
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#define maxn 200010
#define inf 1e18
using namespace std;
struct yts
{
long long x,y;
}p[maxn],tmp[maxn];
bool operator<(yts x,yts y)
{
return x.x<y.x || (x.x==y.x && x.y<y.y);
}
double operator*(yts x,yts y)
{
double X1=x.x,X2=y.x,Y1=x.y,Y2=y.y;
return X1*Y2-X2*Y1;
}
yts operator-(yts x,yts y)
{
yts ans;
ans.x=x.x-y.x;ans.y=x.y-y.y;
return ans;
}
struct yts1
{
int l,r;
int size;
bool flag;
vector<yts> v;
}t[4*maxn];
int st[maxn],to[maxn],next[maxn],fa[maxn],pre[maxn],g[20][maxn];
long long len[maxn],dep[maxn],f[maxn];
int seq[maxn],head[maxn],e[maxn],rank[maxn],size[maxn],dd[maxn],d[maxn];
long long P[maxn],Q[maxn],L[maxn];
bool vis[maxn];
int n,m,num,tot,T;
void addedge(int x,int y,long long z)
{
num++;to[num]=y;len[num]=z;next[num]=st[x];st[x]=num;
}
void dfs1(int x)
{
e[++tot]=x;dd[x]=0;size[x]=1;
for (int p=st[x];p;p=next[p])
{
dep[to[p]]=dep[x]+len[p];d[to[p]]=d[x]+1;
dfs1(to[p]);
size[x]+=size[to[p]];
if (size[to[p]]>size[dd[x]]) dd[x]=to[p];
}
}
int cal(int x)
{
int ans=x;
for (int k=19;k>=0;k--)
if (g[k][ans] && dep[x]-dep[g[k][ans]]<=L[x]) ans=g[k][ans];
return ans;
}
void build(int i,int l,int r)
{
t[i].l=l;t[i].r=r;t[i].flag=0;
yts x;x.x=0;x.y=0;
for (int j=t[i].l;j<=t[i].r+1;j++) t[i].v.push_back(x);
if (l==r) return;
int mid=(l+r)/2;
build(i*2,l,mid);build(i*2+1,mid+1,r);
}
void build(int i)
{
int size=0;
for (int j=t[i].l;j<=t[i].r;j++) tmp[++size]=p[j];
sort(tmp+1,tmp+size+1);
t[i].size=0;
for (int j=1;j<=size;j++)
{
while (t[i].size>1 && (tmp[j]-t[i].v[t[i].size])*(t[i].v[t[i].size]-t[i].v[t[i].size-1])>=0) t[i].size--;
t[i].v[++t[i].size]=tmp[j];
}
}
long long calc(int i,int j,int id)
{
return t[i].v[j].y+(dep[id]-t[i].v[j].x)*P[id]+Q[id];
}
long long query(int i,int id)
{
long long ans=inf;
int l=1,r=t[i].size;
while (r-l>=3)
{
int mid=l+(r-l)/3,midmid=r-(r-l)/3;
if (calc(i,mid,id)<calc(i,midmid,id)) r=midmid; else l=mid;
}
for (int j=l;j<=r;j++) ans=min(ans,calc(i,j,id));
return ans;
}
long long query(int i,int l,int r,int id)
{
if (l<=t[i].l && t[i].r<=r)
{
if (!t[i].flag) build(i),t[i].flag=1;
return query(i,id);
}
int mid=(t[i].l+t[i].r)/2;
long long ans=inf;
if (l<=mid) ans=min(ans,query(i*2,l,r,id));
if (mid<r) ans=min(ans,query(i*2+1,l,r,id));
return ans;
}
long long query(int x,int y,int id)
{
long long ans=inf;
while (d[head[x]]>d[y])
{
ans=min(ans,query(1,rank[head[x]],rank[x],id));x=fa[head[x]];
}
ans=min(ans,query(1,rank[y],rank[x],id));
return ans;
}
int main()
{
scanf("%d%d",&n,&T);
for (int i=2;i<=n;i++)
{
int x;
scanf("%d%lld%lld%lld%lld",&fa[i],&x,&P[i],&Q[i],&L[i]);
addedge(fa[i],i,x);
}
dfs1(1);
int qwer=0;
for (int i=1;i<=n;i++)
if (!vis[e[i]])
{
int k=e[i];
while (k)
{
seq[++qwer]=k;head[k]=e[i];vis[k]=1;k=dd[k];
}
}
for (int i=1;i<=n;i++) rank[seq[i]]=i,g[0][i]=fa[i];
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++)
g[j][i]=g[j-1][g[j-1][i]];
for (int i=1;i<=n;i++) pre[i]=cal(i);
build(1,1,n);
f[1]=0;dep[1]=0;p[rank[1]].x=0;p[rank[1]].y=0;
for (int i=2;i<=n;i++)
{
f[i]=query(fa[i],pre[i],i);
p[rank[i]].x=dep[i];p[rank[i]].y=f[i];
printf("%lld\n",f[i]);
}
return 0;
}
f[i]=min{f[j]+(dep[i]-dep[j])*p[i]+q[i] } (dep[i]-dep[j]<=li)
=f[j]-dep[j]*p[i]+dep[i]*p[i]+q[i]
f[j]=dep[j]*p[i]+f[i]-dep[i]*p[i]-q[i]
f[i]-dep[i]*p[i]-q[i]表示过点(dep[j],f[j])的斜率为p[i]的直线在y轴上的截距
因为p[i]>=0,所以答案一定在下凸壳上
pre[i]表示i最多能延伸到的祖先,这个可以二分什么的乱搞出来
求f[i]就是在fa[i]到pre[i]之间形成的凸壳上三分
树链剖分+线段树维护凸壳
线段树的每个节点暴力建出凸壳,复杂度O(nlog^2n)
每次查询按照剖分查就可以了,复杂度O(log^3n)
注意,本题算叉积的时候会爆longlong,要转成double
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#define maxn 200010
#define inf 1e18
using namespace std;
struct yts
{
long long x,y;
}p[maxn],tmp[maxn];
bool operator<(yts x,yts y)
{
return x.x<y.x || (x.x==y.x && x.y<y.y);
}
double operator*(yts x,yts y)
{
double X1=x.x,X2=y.x,Y1=x.y,Y2=y.y;
return X1*Y2-X2*Y1;
}
yts operator-(yts x,yts y)
{
yts ans;
ans.x=x.x-y.x;ans.y=x.y-y.y;
return ans;
}
struct yts1
{
int l,r;
int size;
bool flag;
vector<yts> v;
}t[4*maxn];
int st[maxn],to[maxn],next[maxn],fa[maxn],pre[maxn],g[20][maxn];
long long len[maxn],dep[maxn],f[maxn];
int seq[maxn],head[maxn],e[maxn],rank[maxn],size[maxn],dd[maxn],d[maxn];
long long P[maxn],Q[maxn],L[maxn];
bool vis[maxn];
int n,m,num,tot,T;
void addedge(int x,int y,long long z)
{
num++;to[num]=y;len[num]=z;next[num]=st[x];st[x]=num;
}
void dfs1(int x)
{
e[++tot]=x;dd[x]=0;size[x]=1;
for (int p=st[x];p;p=next[p])
{
dep[to[p]]=dep[x]+len[p];d[to[p]]=d[x]+1;
dfs1(to[p]);
size[x]+=size[to[p]];
if (size[to[p]]>size[dd[x]]) dd[x]=to[p];
}
}
int cal(int x)
{
int ans=x;
for (int k=19;k>=0;k--)
if (g[k][ans] && dep[x]-dep[g[k][ans]]<=L[x]) ans=g[k][ans];
return ans;
}
void build(int i,int l,int r)
{
t[i].l=l;t[i].r=r;t[i].flag=0;
yts x;x.x=0;x.y=0;
for (int j=t[i].l;j<=t[i].r+1;j++) t[i].v.push_back(x);
if (l==r) return;
int mid=(l+r)/2;
build(i*2,l,mid);build(i*2+1,mid+1,r);
}
void build(int i)
{
int size=0;
for (int j=t[i].l;j<=t[i].r;j++) tmp[++size]=p[j];
sort(tmp+1,tmp+size+1);
t[i].size=0;
for (int j=1;j<=size;j++)
{
while (t[i].size>1 && (tmp[j]-t[i].v[t[i].size])*(t[i].v[t[i].size]-t[i].v[t[i].size-1])>=0) t[i].size--;
t[i].v[++t[i].size]=tmp[j];
}
}
long long calc(int i,int j,int id)
{
return t[i].v[j].y+(dep[id]-t[i].v[j].x)*P[id]+Q[id];
}
long long query(int i,int id)
{
long long ans=inf;
int l=1,r=t[i].size;
while (r-l>=3)
{
int mid=l+(r-l)/3,midmid=r-(r-l)/3;
if (calc(i,mid,id)<calc(i,midmid,id)) r=midmid; else l=mid;
}
for (int j=l;j<=r;j++) ans=min(ans,calc(i,j,id));
return ans;
}
long long query(int i,int l,int r,int id)
{
if (l<=t[i].l && t[i].r<=r)
{
if (!t[i].flag) build(i),t[i].flag=1;
return query(i,id);
}
int mid=(t[i].l+t[i].r)/2;
long long ans=inf;
if (l<=mid) ans=min(ans,query(i*2,l,r,id));
if (mid<r) ans=min(ans,query(i*2+1,l,r,id));
return ans;
}
long long query(int x,int y,int id)
{
long long ans=inf;
while (d[head[x]]>d[y])
{
ans=min(ans,query(1,rank[head[x]],rank[x],id));x=fa[head[x]];
}
ans=min(ans,query(1,rank[y],rank[x],id));
return ans;
}
int main()
{
scanf("%d%d",&n,&T);
for (int i=2;i<=n;i++)
{
int x;
scanf("%d%lld%lld%lld%lld",&fa[i],&x,&P[i],&Q[i],&L[i]);
addedge(fa[i],i,x);
}
dfs1(1);
int qwer=0;
for (int i=1;i<=n;i++)
if (!vis[e[i]])
{
int k=e[i];
while (k)
{
seq[++qwer]=k;head[k]=e[i];vis[k]=1;k=dd[k];
}
}
for (int i=1;i<=n;i++) rank[seq[i]]=i,g[0][i]=fa[i];
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++)
g[j][i]=g[j-1][g[j-1][i]];
for (int i=1;i<=n;i++) pre[i]=cal(i);
build(1,1,n);
f[1]=0;dep[1]=0;p[rank[1]].x=0;p[rank[1]].y=0;
for (int i=2;i<=n;i++)
{
f[i]=query(fa[i],pre[i],i);
p[rank[i]].x=dep[i];p[rank[i]].y=f[i];
printf("%lld\n",f[i]);
}
return 0;
}
相关文章推荐
- Linux下.a库文件的调用
- Android应用中使用ViewPager实现类似QQ的界面切换效果
- CH Round #65 - MFOI杯水题欢乐赛 day2 树形DP+SG函数+线段树合并+DP
- 压缩和解压zip文件
- select count(*)和select count(1)的区别
- CSS direction 使用
- AceDeceiver成为首个可利用苹果DRM设计漏洞感染iOS设备的木马
- Android4.4 xposed安装失败(the xposed framework is not installed)
- Git Bash+EGit在项目中配合使用最常用方法总结(根据场景使用)
- 关于爱情
- 相册
- php扩展开发1--添加函数
- java中关于log日志
- JS解析json数据
- Spring3 MVC请求参数获取的几种方法
- Objective-C @encode关键字
- Android Activity.runOnUiThread() 和 Handler
- LVS-NAT配置安装
- linux java 环境变量配置
- Python 数据结构与算法——快排