[BZOJ4422][Cerc2015]Cow Confinement(差分+扫描线+线段树)

题目描述

传送门

题解

对于每一个点,维护(i,j)可以采到但是(i+1,j)不能采到的花的数量f[i][j]。
那么对于每一只牛,答案就应为f[i][j]+f[i+1][j]+……+f[k][j],k为i向下遇到的第一个栅栏。我们称这个答案为“从点(i,j)出发的答案”。
区间的操作很容易想到线段树,我们考虑扫描线。从右向左扫,用线段树维护列上的答案。遇到一个花(i,j)就相当于在线段树的i位置+1,遇到一头牛(i,j)就查询从这个点出发的答案。如果遇到一个栅栏的右边界,假设边界的范围是(i1,j)(i2,j),那么应该求出[i1,i2]的答案,然后加到i1-1去。如果遇到一个栅栏的左边界,假设边界的范围是(i1,j1)(i2,j1),且当前栅栏的右边界的范围是(i3,j2)(i4,j2)那么应该将[i1,i2]段清零,并且在点i1-1减去从点(i4+1,j2)出发的答案。本质上是个差分。
对于扫到的同一列,维护的顺序问题需要注意,并且细节问题比较多。

代码

#include
#include
#include
#include
using namespace std;

const int N=2e5+5;
const int LOC=1e6+5;
const int NN=N*10;
const int T=LOC*4;

int f,m,n,maxr,maxc,cnt;
int r1[N],c1[N],r2[N],c2[N],fr[N],fc[N],cor[N],coc[N],nearby[N],Nearby[N],last[N],Ans[N];
struct hp{int r1,r2,c1,c2,r,c,id,ty;}a[NN];

inline int in()
{
    char ch=getchar(); int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline void out(int x)
{
    if (x==0)
    {
        putchar('0');
        putchar('\n');
        return;
    }
    int now=0;
    int print[10];
    while (x>0)
    {
        print[++now]=x%10;
        x/=10;
    }
    for (int i=now;i>=1;--i) putchar(print[i]+'0');
    putchar('\n');
    return;
}
inline int cmp1(hp a,hp b){return a.cinline int cmp2(hp a,hp b){return a.c>b.c||(a.c==b.c&&a.tynamespace Junior
{
    int lef[T];
    inline void update(int now)
    {
        lef[now]=min(lef[now<<1],lef[now<<1|1]);
    }
    inline void build(int now,int l,int r)
    {
        int mid=(l+r)>>1;
        if (l==r)
        {
            lef[now]=maxr+1;
            return;
        }
        build(now<<1,l,mid);
        build(now<<1|1,mid+1,r);
        update(now);
    }
    inline void point_change(int now,int l,int r,int x,int v)
    {
        int mid=(l+r)>>1;
        if (l==r)
        {
            if (v==1) lef[now]=l;
            else lef[now]=maxr+1;
            return;
        }
        if (x<=mid) point_change(now<<1,l,mid,x,v);
        else point_change(now<<1|1,mid+1,r,x,v);
        update(now);
    }
    inline int query(int now,int l,int r,int lrange,int rrange)
    {
        int mid=(l+r)>>1,ans=maxr+1;
        if (lrange<=l&&r<=rrange) return lef[now];
        if (lrange<=mid) ans=min(ans,query(now<<1,l,mid,lrange,rrange));
        if (mid+1<=rrange) ans=min(ans,query(now<<1|1,mid+1,r,lrange,rrange));
        return ans;
    }
}
namespace Senior
{
    int sum[T],delta[T];
    inline void update(int now)
    {
        sum[now]=sum[now<<1]+sum[now<<1|1];
    }
    inline void pushdown(int now,int l,int r,int mid)
    {
        if (delta[now]!=-1)
        {
            sum[now<<1]=delta[now]; delta[now<<1]=delta[now];
            sum[now<<1|1]=delta[now]; delta[now<<1|1]=delta[now];
            delta[now]=-1;
        }
    }
    inline void point_change(int now,int l,int r,int x,int v)
    {
        int mid=(l+r)>>1;
        if (l==r)
        {
            sum[now]+=v;
            return;
        }
        pushdown(now,l,r,mid);
        if (x<=mid) point_change(now<<1,l,mid,x,v);
        else point_change(now<<1|1,mid+1,r,x,v);
        update(now);
    }
    inline void interval_cover(int now,int l,int r,int lrange,int rrange,int v)
    {
        int mid=(l+r)>>1;
        if (lrange<=l&&r<=rrange)
        {
            sum[now]=v;
            delta[now]=v;
            return;
        }
        pushdown(now,l,r,mid);
        if (lrange<=mid) interval_cover(now<<1,l,mid,lrange,rrange,v);
        if (mid+1<=rrange) interval_cover(now<<1|1,mid+1,r,lrange,rrange,v);
        update(now);
    }
    inline int query(int now,int l,int r,int lrange,int rrange)
    {
        int mid=(l+r)>>1,ans=0;
        if (lrange<=l&&r<=rrange) return sum[now]; 
        pushdown(now,l,r,mid);
        if (lrange<=mid) ans+=query(now<<1,l,mid,lrange,rrange);
        if (mid+1<=rrange) ans+=query(now<<1|1,mid+1,r,lrange,rrange);
        return ans;
    }
}

int main()
{
    f=in();
    for (int i=1;i<=f;++i)
    {
        r1[i]=in(); c1[i]=in(); r2[i]=in(); c2[i]=in();
        maxr=max(maxr,r1[i]); maxr=max(maxr,r2[i]);
        maxc=max(maxc,c1[i]); maxc=max(maxc,c2[i]);
    }
    m=in();
    for (int i=1;i<=m;++i)
    {
        fr[i]=in(); fc[i]=in();
        maxr=max(maxr,fr[i]); maxc=max(maxc,fc[i]);
    }
    n=in();
    for (int i=1;i<=n;++i)
    {
        cor[i]=in(); coc[i]=in();
        maxr=max(maxr,cor[i]); maxc=max(maxc,coc[i]);
    }

    cnt=0;
    for (int i=1;i<=f;++i)
    {
        if (r1[i]!=1) a[++cnt].r=r1[i]-1,a[cnt].c=c1[i],a[cnt].ty=1,a[++cnt].r=r1[i]-1,a[cnt].c=c2[i],a[cnt].ty=4;
        a[++cnt].r=r2[i],a[cnt].c=c1[i],a[cnt].ty=1,a[++cnt].r=r2[i],a[cnt].c=c2[i],a[cnt].ty=4;
        if (c2[i]!=maxc) a[++cnt].r=r1[i],a[cnt].c=c2[i]+1,a[cnt].ty=3,a[cnt].id=i;
    }
    for (int i=1;i<=n;++i) a[++cnt].r=cor[i],a[cnt].c=coc[i],a[cnt].ty=2,a[cnt].id=i;
    sort(a+1,a+cnt+1,cmp1);
    Junior::build(1,1,maxr);
    for (int i=1;i<=cnt;++i)
    {
        switch(a[i].ty)
        {
            case 1://左栅栏 
                {
                    Junior::point_change(1,1,maxr,a[i].r,1);
                    break;
                }
            case 2://牛 
                {
                    nearby[a[i].id]=Junior::query(1,1,maxr,a[i].r,maxr);
                    if (nearby[a[i].id]==maxr+1) nearby[a[i].id]=maxr;
                    break;
                }
            case 3://栅栏的右上角 
                {
                    Nearby[a[i].id]=Junior::query(1,1,maxr,a[i].r,maxr);
                    if (Nearby[a[i].id]==maxr+1) Nearby[a[i].id]=maxr;
                    break;
                }
            case 4://右栅栏 
                {
                    Junior::point_change(1,1,maxr,a[i].r,-1);
                    break;
                }
        }
    }
    cnt=0;
    for (int i=1;i<=f;++i)
    {
        a[++cnt].r1=r1[i],a[cnt].c1=c1[i],a[cnt].r2=r2[i],a[cnt].c2=c1[i],a[cnt].ty=4,a[cnt].id=i;
        a[cnt].r=a[cnt].r1,a[cnt].c=a[cnt].c1;
        if (c2[i]!=maxc)
        {
            a[++cnt].r1=r1[i],a[cnt].c1=c2[i]+1,a[cnt].r2=r2[i],a[cnt].c2=c2[i]+1,a[cnt].ty=3,a[cnt].id=i;
            a[cnt].r=a[cnt].r1,a[cnt].c=a[cnt].c1;
        }
    }
    for (int i=1;i<=m;++i) a[++cnt].r=fr[i],a[cnt].c=fc[i],a[cnt].ty=1;
    for (int i=1;i<=n;++i) a[++cnt].r=cor[i],a[cnt].c=coc[i],a[cnt].ty=2,a[cnt].id=i;
    sort(a+1,a+cnt+1,cmp2);
    memset(Senior::delta,-1,sizeof(Senior::delta));
    for (int i=1;i<=cnt;++i)
    {
        switch(a[i].ty)
        {
            case 1://花 
                {
                    Senior::point_change(1,1,maxr,a[i].r,1);
                    break;
                }
            case 2://牛 
                {
                    int ans=Senior::query(1,1,maxr,a[i].r,nearby[a[i].id]);
                    Ans[a[i].id]=ans;
                    break;
                }
            case 3://右栅栏 
                {
                    if (a[i].r2!=maxr) last[a[i].id]=Senior::query(1,1,maxr,a[i].r2+1,Nearby[a[i].id]);
                    int ans=Senior::query(1,1,maxr,a[i].r1,Nearby[a[i].id]);
                    if (a[i].r1!=1) Senior::point_change(1,1,maxr,a[i].r1-1,ans);
                    Senior::interval_cover(1,1,maxr,a[i].r1,a[i].r2,0);
                    break;
                }
            case 4://左栅栏 
                {

                    Senior::interval_cover(1,1,maxr,a[i].r1,a[i].r2,0);
                    if (a[i].r1!=1) Senior::point_change(1,1,maxr,a[i].r1-1,-last[a[i].id]);
                    break;
                }
        }
    }
    for (int i=1;i<=n;++i)
        out(Ans[i]);

总结

1、一定要想好了再写。
2、代码能力太弱= =

你可能感兴趣的:(题解,线段树,扫描线)