题解:CF780G Andryusha and Nervous Barriers

题目链接:洛谷 Codeforces


既然没有 CDQ 分治的题解,那我就来水一篇。

思路

考虑确定从一个隔板边缘掉落会到哪个隔板上,以及从每个 \((h+1,i)\) 释放会到哪个隔板上。

首先,如果没有穿过隔板的限制,一个简单的做法就是按 \(h\) 轴从小到大做扫描线,用线段树等数据结构维护 \(w\) 轴每个位置当前在最上面的隔板编号。

再考虑加上穿过隔板的限制。设当前询问从 \((x,y)\) 会落到哪个隔板上,这个限制就相当于要求 \(x\leq u_i+s_i\)。这可以 CDQ 分治,设每个隔板的权值为 \(u_i+s_i\),每个询问的权值为 \(x\),将所有隔板和询问按权值降序排序(如果有相同的权值则隔板靠前。),则只有左边的隔板能作为右边询问的答案,然后就是公式化的 CDQ 分治,在每个分治层对左边的隔板和右边的询问做上述扫描线即可。

确定了从一个隔板会掉到哪个隔板上后,就可以按掉落的方向建图,显然图是一个 DAG,按拓扑序简单 dp 即可求出答案。

时间复杂度 \(O((n+w)\log(n+w)\log w)\)

代码

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define ll long long
const int N=1e5+5,P=1e9+7,inf=2e9;

int h,w,n,q,to[N*3],in[N*2],f[N*2];
vector<int> e[N*2];
struct ball
{
    int u,l,r,s;
}a[N];
struct drop
{
    int op,id,l,r,h,mx;
}b[N*4];

namespace sgt
{
    int val[N*4],tag[N*4];

    void init()
    {
        val[1]=tag[1]=0;
    }

    void pushdown(int x)
    {
        if(tag[x]!=-1)
        {
            tag[x*2]=tag[x*2+1]=val[x*2]=val[x*2+1]=tag[x];
            tag[x]=-1;
        }
    }

    void update(int pl,int pr,int v,int x=1,int dl=1,int dr=w)
    {
        if(dl>=pl&&dr<=pr)
        {
            val[x]=tag[x]=v;
            return;
        }
        pushdown(x);
        int mid=(dl+dr)>>1;
        if(mid>=pl)
            update(pl,pr,v,x*2,dl,mid);
        if(mid<pr)
            update(pl,pr,v,x*2+1,mid+1,dr);
    }

    int query(int p,int x=1,int dl=1,int dr=w)
    {
        if(dl==dr)
            return val[x];
        pushdown(x);
        int mid=(dl+dr)>>1;
        if(mid>=p)
            return query(p,x*2,dl,mid);
        return query(p,x*2+1,mid+1,dr);
    }
}

void solve(int l,int r)
{
    if(l==r)
        return;
    int mid=(l+r)>>1;
    solve(l,mid);
    solve(mid+1,r);
    sort(b+l,b+mid+1,[](drop u,drop v){return u.h<v.h;});
    sort(b+mid+1,b+r+1,[](drop u,drop v){return u.h<v.h;});
    sgt::init();
    int j=l;
    for(int i=mid+1;i<=r;i++)
    {
        while(j<=mid&&b[j].h<b[i].h)
        {
            if(!b[j].op)
                sgt::update(b[j].l,b[j].r,b[j].id);
            j++;
        }
        if(b[i].op)
        {
            int p=sgt::query(b[i].l);
            if(a[p].u>a[to[b[i].op]].u)
                to[b[i].op]=p;
        }
    }
}

signed main()
{
    scanf("%d%d%d",&h,&w,&n);
    for(int i=1;i<=w*4;i++)
        sgt::tag[i]=-1;
    int tot=0;
    for(int i=1;i<=w;i++)
        b[++q]={++tot,n+i,i,0,h+1,h+1};
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&a[i].u,&a[i].l,&a[i].r,&a[i].s);
        b[++q]={0,i,a[i].l,a[i].r,a[i].u,a[i].u+a[i].s};
        b[++q]={++tot,i,a[i].l>1?a[i].l-1:a[i].r+1,0,a[i].u,a[i].u};
        b[++q]={++tot,i,a[i].r<w?a[i].r+1:a[i].l-1,0,a[i].u,a[i].u};
    }
    sort(b+1,b+1+q,[](drop u,drop v){return u.mx==v.mx?u.op<v.op:u.mx>v.mx;});
    solve(1,q);
    tot=0;
    for(int i=1;i<=w;i++)
    {
        e[n+i].push_back(to[++tot]);
        in[to[tot]]++;
    }
    for(int i=1;i<=n;i++)
    {
        e[i].push_back(to[++tot]);
        in[to[tot]]++;
        e[i].push_back(to[++tot]);
        in[to[tot]]++;
    }
    queue<int> que;
    for(int i=1;i<=n+w;i++)
        if(!in[i])
        {
            que.push(i);
            if(i>n)
                f[i]++;
        }
    while(que.size())
    {
        int x=que.front();
        que.pop();
        for(int y:e[x])
        {
            (f[y]+=f[x])%=P;
            in[y]--;
            if(!in[y])
                que.push(y);
        }
    }
    printf("%d",f[0]);
    return 0;
}
posted @ 2026-09-15 10:19  ShanLing3  阅读(3)  评论(0)    收藏  举报
//雪花飘落效果