bzoj4311(向量 线段树分治+每次重新构造凸壳三分更新答案)

该博客讨论了一种基于向量和线段树分治的方法来处理动态更新凸壳的问题。与二分图不同,由于无法回退且需要保持横坐标排序的凸壳结构,博主提出通过将向量时间段插入时间线段树,并在每个节点处构造凸壳,对查询进行三分更新答案,确保所有影响因素都被考虑在内。
摘要由CSDN通过智能技术生成

题目
在这里插入图片描述二分图不同的是:1. 没办法回退。。 2.就算回退了 也不能快速与其他修改连成关系,不同于并查集。因为要与之前的这个节点的修改 按横坐标排序 构造凸壳。
就需要一段一段的被更新。
先把每个向量存在的时间段插入到时间线段树中。然后答案的更新被分成好多部分更新。每到线段树一个节点,用这个节点存在的向量的构成上凸壳(可以画画图就是对的),然后这个节点对应时间段里的查询 在这个凸壳上三分更新答案。这样一定不会遗忘掉。。比如时间 i 的询问 q[i]那么对他影响的修改的[t1,t2]
t1<=i<=t2 那么 t1一定在其中一个 这个修改在线段树分段 中。就会被query…

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2e5+5;
struct Update{int x,y;}u[N];
int tim[N];
struct Query{int x,y;}q[N];
vector<int>vec[N<<2];
void ins(int id,int cl,int cr,int l,int r,int pos){
    if(cl<=l&&r<=cr) {vec[pos].push_back(id);return;}
    int mid=(l+r)>>1;
    if(cl<=mid) ins(id,cl,cr,l,mid,pos<<1);
    if(cr>mid) ins(id,cl,cr,mid+1,r,pos<<1|1);
}
inline int cmp(int id1,int id2){//注意x相同时y小的在前面
    return (u[id1].x==u[id2].x)?(u[id1].y<=u[id2].y):(u[id1].x<=u[id2].x);
}
inline ll cal(int i,int j,int k){//维护斜率递减的上凸壳
    return (ll)(u[k].y-u[i].y)*(u[j].x-u[i].x)>(ll)(u[j].y-u[i].y)*(u[k].x-u[i].x);
}
inline ll dj(int i,int j){//i j两点对应的点积  注意i是询问q数组 j是修改的,用来更新询问 是u数组
    return ((ll)q[i].x*u[j].x)+((ll)u[j].y*q[i].y);
}
int st[N];ll ans[N];
inline void query(int id,int top){//在凸壳上三分
    int low=1,high=top,mid1,mid2;
    while(high-low>=3){
        mid1=low+(high-low)/3,mid2=high-(high-low)/3;
        if(dj(id,st[mid1])<=dj(id,st[mid2])) low=mid1;
        else high=mid2;
    }
    for(int i=low;i<=high;++i) ans[id]=max(ans[id],dj(id,st[i]));
}
void divide(int l,int r,int pos){
    sort(vec[pos].begin(),vec[pos].end(),cmp);//为了构造上凸壳
    int top=0;
    for(int i=0;i<vec[pos].size();++i){
        int d=vec[pos][i];
        while(top>=2&&cal(st[top-1],st[top],d)) --top;
        st[++top]=d;
    }
    for(int i=l;i<=r;++i)
        if(q[i].x) query(i,top);//时间在[l,r]的查询都需要被上述凸壳更新 因为上面的凸壳在时间[l,r]一直存在
    if(l==r) return;
    int mid=(l+r)>>1;
    divide(l,mid,pos<<1),divide(mid+1,r,pos<<1|1);
}
int main(){
    int n;scanf("%d",&n);
    int tot=0;
    for(int i=1;i<=n;++i){
        int f;scanf("%d",&f);
        if(f==1) ++tot,scanf("%d%d",&u[tot].x,&u[tot].y),tim[tot]=i;
        if(f==2){
            int d;scanf("%d",&d);//删除第d个添加进来的
            ins(d,tim[d],i,1,n,1),tim[d]=0;
        }
        if(f==3) scanf("%d%d",&q[i].x,&q[i].y);
    }
    for(int i=1;i<=tot;++i)
        if(tim[i]) ins(i,tim[i],n,1,n,1);
    divide(1,n,1);
    for(int i=1;i<=n;++i)
        if(q[i].x) printf("%lld\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值