Problem - F - Codeforces 线段树二分

180 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 24 天,点击查看活动详情

Problem - F - Codeforces 线段树二分

 如果第i个线段的[l,r]内还有颜色的话答案就是0,否则就要向左去找离l最近的点,向右去找离r最近的点,然后两个值再比较;

这样大概思路其实就有了,我们可以写一个区间加法区间求和的线段树,然后对出现的每个区间都加1,这样枚举到第i个线段[l,r]时,第i个线段的颜色为c,那么将所有颜色为c的线段区间都-1,也就是都去掉,然后如果[l,r]的和还是1的话说明有相交的线段,答案就是0,否则去左边[1,l]找离l最近的点,我们可以根据区间的和来判断这个区间的走向,因为是离l最近,那么优先找的区间一定是[(1+l),l],也就是右半段,放在函数里就是[mid+1,r],如果区间和大于等于1的话说明这个区间里一定是有符合条件的点的,那就再去这个区间搜,否则就要去[l,mid]区间里搜,对于去找右边离r最近的点的方法也是一样的;注意一个关键的小点,枚举答案的时候因为颜色是小于等于n的,所以可以直接去枚举颜色而不是去枚举线段,这样会减少很多复杂度;虽然这种方式好想,但是因为用了query函数所以多了个log,总的复杂度是o(nlognlogn);

l,r的范围比较大,离散化一下就可以,注意求答案做差的时候需要是原来的值做差,而不是离散后的

o(nlognlogn)的代码

struct segtree
{
    int val,lazy;
}tr[400005<<3];
void pushup(int p){tr[p].val=tr[p<<1].val+tr[p<<1|1].val;}
void build(int l,int r,int p)
{
    tr[p].lazy=0;
    if(l==r)
    {
        tr[p].val=0;
        return;
    }
    int mid=l+r>>1;
    build(l,mid,p<<1);
    build(mid+1,r,p<<1|1);
    pushup(p);
}
void pushdown(int p,int ln,int rn)
{
    if(tr[p].lazy)
    {
        tr[p<<1].lazy+=tr[p].lazy;
        tr[p<<1|1].lazy+=tr[p].lazy;
        tr[p<<1].val+=tr[p].lazy*ln;
        tr[p<<1|1].val+=tr[p].lazy*rn;
        tr[p].lazy=0;
    }
}
void update(int L,int R,int C,int l,int r,int p)
{
    if(L<=l&&r<=R)
    {
        tr[p].val+=C*(r-l+1);
        tr[p].lazy+=C;
        return;
    }
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    if(L<=mid) update(L,R,C,l,mid,p<<1);
    if(R>mid) update(L,R,C,mid+1,r,p<<1|1);
    pushup(p);
}
int query(int L,int R,int l,int r,int p)
{
    if(L<=l&&r<=R) return tr[p].val;
    if(L>r||R<l) return 0;
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    return query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
struct node
{
    int l,r,c,id;
}a[400005];
vector<node>v[400005];
int t,n,b[400005],m,ans[400005];
void oper(int col,int x)
{
    for(int i=0;i<v[col].size();i++)
    {
        int l=v[col][i].l,r=v[col][i].r;
        update(l,r,x,1,m,1);
    }
}
int check1(int l,int r)
{
    if(query(l,r,1,m,1)<=0) return inf;
    if(l==r) return l;
    int mid=l+r>>1;
    if(query(mid+1,r,1,m,1)>0) return check1(mid+1,r);
    else return check1(l,mid);
}
int check2(int l,int r)
{
    if(query(l,r,1,m,1)<=0) return inf;
    if(l==r) return l;
    int mid=l+r>>1;
    if(query(l,mid,1,m,1)>0) return check2(l,mid);
    else return check2(mid+1,r);
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    //freopen("in.txt", "r", stdin);
    cin>>t;
    while (t--)
    {
        cin>>n;
        m=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i].l>>a[i].r>>a[i].c;
            b[++m]=a[i].l;b[++m]=a[i].r;
            v[i].clear();
        }
        sort(b+1,b+m+1);
        m=unique(b+1,b+m+1)-b-1;
        build(1,m,1);
        for(int i=1;i<=n;i++)
        {
            int l=lower_bound(b+1,b+m+1,a[i].l)-b;
            int r=lower_bound(b+1,b+m+1,a[i].r)-b;
            v[a[i].c].push_back(node{l,r,a[i].c,i});
            update(l,r,1,1,m,1);
        }
        for(int col=1;col<=n;col++)
        {
            oper(col,-1);
            for(int i=0;i<v[col].size();i++)
            {
                int l=v[col][i].l,r=v[col][i].r,id=v[col][i].id;
                ans[id]=inf;
                if(query(l,r,1,m,1)>0)
                {
                    ans[id]=0;
                    continue;
                }
                int c1=check1(1,l),c2=check2(r,m);
                if(c1!=inf) ans[id]=min(ans[id],b[l]-b[c1]);
                if(c2!=inf) ans[id]=min(ans[id],b[c2]-b[r]);
            }
            oper(col,1);
        }
        for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
        cout<<endl;
    }
    return 0;
}

还有一种方式是用multiset来二分的,将所有的l和r存入容器中,然后枚举的时候二分去找离l和r最近的点,感觉这个方法的复杂度是o(nlogn)的,但是好像没有想象中的那么优秀,就比上一个方法快100多ms;

Codeforces Round #826 (Div. 3) F // 线段树 - Jakon - 博客园 (cnblogs.com)

struct segtree
{
    int val,lazy;
}tr[400005<<3];
void pushup(int p){tr[p].val=tr[p<<1].val+tr[p<<1|1].val;}
void build(int l,int r,int p)
{
    tr[p].lazy=0;
    if(l==r)
    {
        tr[p].val=0;
        return;
    }
    int mid=l+r>>1;
    build(l,mid,p<<1);
    build(mid+1,r,p<<1|1);
    pushup(p);
}
void pushdown(int p,int ln,int rn)
{
    if(tr[p].lazy)
    {
        tr[p<<1].lazy+=tr[p].lazy;
        tr[p<<1|1].lazy+=tr[p].lazy;
        tr[p<<1].val+=tr[p].lazy*ln;
        tr[p<<1|1].val+=tr[p].lazy*rn;
        tr[p].lazy=0;
    }
}
void update(int L,int R,int C,int l,int r,int p)
{
    if(L<=l&&r<=R)
    {
        tr[p].val+=C*(r-l+1);
        tr[p].lazy+=C;
        return;
    }
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    if(L<=mid) update(L,R,C,l,mid,p<<1);
    if(R>mid) update(L,R,C,mid+1,r,p<<1|1);
    pushup(p);
}
int query(int L,int R,int l,int r,int p)
{
    if(L<=l&&r<=R) return tr[p].val;
    if(L>r||R<l) return 0;
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    return query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
struct node
{
    int l,r,c,id;
}a[200005];
vector<node>v[200005];
multiset<int>L,R;
int t,n,b[400005],m,ans[400005];
void oper(int col,int x)
{
    for(int i=0;i<v[col].size();i++)
    {
        int l=v[col][i].l,r=v[col][i].r;
        update(l,r,x,1,m,1);
    }
    for(int i=0;i<v[col].size();i++)
    {
        int l=b[v[col][i].l],r=b[v[col][i].r];
        if(x==-1)
        {
            L.erase(L.find(l));R.erase(R.find(r));
        }
        else
        {
            L.insert(l);R.insert(r);
        }
    }
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    //freopen("in.txt", "r", stdin);
    cin>>t;
    while (t--)
    {
        cin>>n;
        m=0;
        L.clear();R.clear();
        for(int i=1;i<=n;i++)
        {
            cin>>a[i].l>>a[i].r>>a[i].c; a[i].id=i;
            b[++m]=a[i].l;b[++m]=a[i].r;
            L.insert(a[i].l);R.insert(a[i].r);
            v[i].clear();
        }
        sort(b+1,b+m+1);
        m=unique(b+1,b+m+1)-b-1;
        build(1,m,1);
        for(int i=1;i<=n;i++)
        {
            int l=lower_bound(b+1,b+m+1,a[i].l)-b;
            int r=lower_bound(b+1,b+m+1,a[i].r)-b;
            v[a[i].c].push_back(node{l,r,a[i].c,i});
            update(l,r,1,1,m,1);
        }
        for(int col=1;col<=n;col++)
        {
            if(v[col].size()==0) continue;
            oper(col,-1);
            for(int i=0;i<v[col].size();i++)
            {
                int l=v[col][i].l,r=v[col][i].r,id=v[col][i].id;
                ans[id]=inf;
                if(query(l,r,1,m,1)>0)
                {
                    ans[id]=0;
                    continue;
                }
                auto it1=R.lower_bound(a[id].l);
                if(it1!=R.begin()) ans[id]=min(ans[id],a[id].l-*prev(it1));
                auto it2=L.lower_bound(a[id].r);
                if(it2!=L.end()) ans[id]=min(ans[id],*it2-a[id].r);
            }
            oper(col,1);
        }
        for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
        cout<<endl;
    }
    return 0;
}

最后一种就是真正的o(nlogn)了,去掉了query函数,直接是写了一个二分函数,看懂了第一个方法,再看这个的写法也比较容易了,这其实也算是一个查询的函数了,不过这是查询下标而已

云剪贴板 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

上面的链接来自(26 封私信 / 85 条消息) 严格鸽 - 知乎 (zhihu.com)

struct segtree
{
    int val,lazy;
}tr[400005<<3];
void pushup(int p){tr[p].val=tr[p<<1].val+tr[p<<1|1].val;}
void build(int l,int r,int p)
{
    tr[p].lazy=0;
    if(l==r)
    {
        tr[p].val=0;
        return;
    }
    int mid=l+r>>1;
    build(l,mid,p<<1);
    build(mid+1,r,p<<1|1);
    pushup(p);
}
void pushdown(int p,int ln,int rn)
{
    if(tr[p].lazy)
    {
        tr[p<<1].lazy+=tr[p].lazy;
        tr[p<<1|1].lazy+=tr[p].lazy;
        tr[p<<1].val+=tr[p].lazy*ln;
        tr[p<<1|1].val+=tr[p].lazy*rn;
        tr[p].lazy=0;
    }
}
void update(int L,int R,int C,int l,int r,int p)
{
    if(L<=l&&r<=R)
    {
        tr[p].val+=C*(r-l+1);
        tr[p].lazy+=C;
        return;
    }
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    if(L<=mid) update(L,R,C,l,mid,p<<1);
    if(R>mid) update(L,R,C,mid+1,r,p<<1|1);
    pushup(p);
}
int query(int L,int R,int l,int r,int p)
{
    if(L<=l&&r<=R) return tr[p].val;
    if(L>r||R<l) return 0;
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    return query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
struct node
{
    int l,r,c,id;
}a[400005];
vector<node>v[400005];
int t,n,b[400005],m,ans[400005];
void oper(int col,int x)
{
    for(int i=0;i<v[col].size();i++)
    {
        int l=v[col][i].l,r=v[col][i].r;
        update(l,r,x,1,m,1);
    }
}
int check1(int L,int l,int r,int p)
{
    if(l>L) return inf;
    if(r<=L)
    {
        if(tr[p].val<=0) return inf;
        if(l==r) return l;
    }
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    int pos=check1(L,mid+1,r,p<<1|1);
    if(pos!=inf) return pos;
    else return check1(L,l,mid,p<<1);
}
int check2(int R,int l,int r,int p)
{
    if(r<R) return inf;
    if(l>=R)
    {
        if(tr[p].val<=0) return inf;
        if(l==r) return l;
    }
    int mid=l+r>>1;
    pushdown(p,mid-l+1,r-mid);
    int pos=check2(R,l,mid,p<<1);
    if(pos!=inf) return pos;
    else return check2(R,mid+1,r,p<<1|1);
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    //freopen("in.txt", "r", stdin);
    cin>>t;
    while (t--)
    {
        cin>>n;
        m=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i].l>>a[i].r>>a[i].c;
            b[++m]=a[i].l;b[++m]=a[i].r;
            v[i].clear();
        }
        sort(b+1,b+m+1);
        m=unique(b+1,b+m+1)-b-1;
        build(1,m,1);
        for(int i=1;i<=n;i++)
        {
            int l=lower_bound(b+1,b+m+1,a[i].l)-b;
            int r=lower_bound(b+1,b+m+1,a[i].r)-b;
            v[a[i].c].push_back(node{l,r,a[i].c,i});
            update(l,r,1,1,m,1);
        }
        for(int col=1;col<=n;col++)
        {
            oper(col,-1);
            for(int i=0;i<v[col].size();i++)
            {
                int l=v[col][i].l,r=v[col][i].r,id=v[col][i].id;
                ans[id]=inf;
                if(query(l,r,1,m,1)>0)
                {
                    ans[id]=0;
                    continue;
                }
                int c1=check1(l,1,m,1);
                int c2=check2(r,1,m,1);
                if(c1!=inf) ans[id]=min(ans[id],b[l]-b[c1]);
                if(c2!=inf) ans[id]=min(ans[id],b[c2]-b[r]);
            }
            oper(col,1);
        }
        for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
        cout<<endl;
    }
    return 0;
}