码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P5416 [CTSC2016]时空旅行(线段树分治)

时间:2019-09-14 19:26:02      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:define   double   code   ++i   pac   lin   排序   const   简化   

题目
简化题意:你需要维护\(n\)个集合,集合元素为二元组\((x,v)\)。集合\(i\)的产生方式是以某个原有集合\(p_i\)为样本,扩展或删除一个元素后得到新集合。有\(q\)次询问,每次给出\(y\)并指定一个集合\(i\),要求从集合\(i\)中找出一个元素,最小化\((x?y)^2+v\)
先拆式子\((x-y)^2+v=x^2-2xy+y^2+v\),令其等于\(k\)\(x^2+y^2-2xy+v=k\)
移项得\(2yx+k=y^2+x^2+v\),可以看作是\((x,x^2+y^2+v)\)这一决策点,\(2y\)为斜率,\(k\)就为\(y\)轴截距。所以这个可以维护凸壳来解决。
我们知道每一个元素在\(dfs\)序中会以一段一段区间的形式出现。
每个点不管加入还是删除元素都会造成一个新的区间,所以区间总数是\(O(n)\)的。
将元素按照\(x\)升序排序后插入线段树,每个节点维护一个凸壳。
询问按\(y\)升序排序,可以使得每次操作可是直接取上一次凸壳最前面的值。

#include<bits/stdc++.h>
#define LL long long
#define db double
#define pb push_back
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
#define INF 1e18
using namespace std;
const int N=500007;
int L[N<<2],R[N<<2],t[N],dfn[N],T[N],n,m,Time;
LL C[N],ans[N],val[N];
vector<int>G[N],al[N],ar[N],w[N<<2];
struct node{LL x,val,id;}a[N];
int cmp(int a,int b){return val[a]<val[b];}
int operator<(node a,node b){return a.val<b.val;}
LL min(LL a,LL b){return a<b? a:b;}
db K(int x ,int y){return (val[x]*val[x]+C[x]-val[y]*val[y]-C[y])/(db)(val[x]-val[y]);}
void dfs(int u,int fa)
{
    dfn[u]=++Time;
    if(T[u]>0) al[T[u]].pb(Time);
    if(T[u]<0) ar[-T[u]].pb(Time-1);
    for(int i=G[u].size()-1,v;~i;--i) if((v=G[u][i])^fa) dfs(v,u);
    if(T[u]>0) ar[T[u]].pb(Time);
    if(T[u]<0) al[-T[u]].pb(Time+1);
}
void build(int p,int l,int r)
{
    L[p]=0,R[p]=-1;
    if(l==r) return ;
    build(ls,l,mid),build(rs,mid+1,r);
}
void update(int p,int l,int r,int ql,int qr,int P)
{
    if(ql==l&&r==qr)
    {
    while(w[p].size()<=R[p]+5) w[p].pb(0);
    if(L[p]<=R[p]&&val[w[p][R[p]]]==val[P])
    {
        if(C[w[p][R[p]]]<=C[P]) return ;
        --R[p];
    }
    while(L[p]<R[p]&&K(w[p][R[p]],P)<K(w[p][R[p]],w[p][R[p]-1])) --R[p];
    w[p][++R[p]]=P;
    return ;
    }
    if(qr<=mid) update(ls,l,mid,ql,qr,P);
    else if(ql>mid) update(rs,mid+1,r,ql,qr,P);
    else update(ls,l,mid,ql,mid,P),update(rs,mid+1,r,mid+1,qr,P);
}
LL query(int p,int l,int r,int x,LL t,LL sum)
{
    LL ans=INF;
    while(L[p]<R[p]&&K(w[p][L[p]],w[p][L[p]+1])<=2.0*t) ++L[p];
    if(L[p]<=R[p]&&w[p].size()>0) ans=(t-val[w[p][L[p]]])*(t-val[w[p][L[p]]])+C[w[p][L[p]]];
    ans=min(ans,sum);
    if(l==r) return ans;
    return x<=mid? query(ls,l,mid,x,t,ans):query(rs,mid+1,r,x,t,ans);
}
int main()
{
    int i,j,k,u,x,opt,l,r;
    scanf("%d%d%lld",&n,&m,&C[0]);
    for(i=1;i<n;++i) scanf("%d%d%d",&opt,&u,&x),G[u].pb(i),(opt? (T[i]=-x):(T[i]=x,scanf("%lld%d%d%lld",&val[x],&u,&u,&C[x])));
    dfs(0,0),build(1,1,n);
    for(i=1;i<=n;++i) t[i]=i;
    sort(t+1,t+n+1,cmp),update(1,1,n,1,n,0);
    for(i=1;i<=n;++i) for(k=t[i],j=0;j<al[k].size();++j) if((l=al[k][j])<=(r=ar[k][j])) update(1,1,n,l,r,k);
    for(i=1;i<=m;++i) scanf("%lld%lld",&a[i].x,&a[i].val),a[i].id=i;
    sort(a+1,a+m+1);
    for(i=1;i<=m;++i) ans[a[i].id]=query(1,1,n,dfn[a[i].x],a[i].val,INF);
    for(i=1;i<=m;++i) printf("%lld\n",ans[i]);
}

Luogu P5416 [CTSC2016]时空旅行(线段树分治)

标签:define   double   code   ++i   pac   lin   排序   const   简化   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11519553.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!