Bzoj 4422: [Cerc2015]Cow Confinement(线段树+扫描线)

以下内容来自ShallWe's Blog

题目

题目链接
4422: [Cerc2015]Cow Confinement
Description

一个10^6行10^6列的网格图,上面有一些牛、花和一些矩形围栏,围栏在格子的边界上,牛和花在格子里,牛只能向下或向右走,牛也不能穿过围栏和地图边界,求每头牛它能到达的花的数量。注意栅栏不会相交

Input

第一行一个数f表示矩形围栏的数量。
接下来f行,每行四个数x1,y1,x2,y2,表示(x1,y1)在围栏内部矩形的左上角,(x2,y2)在右下角。
接下来一行一个数m表示花的数量。
接下来m行每行两个数x,y,表示在(x,y)处有一朵花。
接下来一行一个数n表示牛的数量。
接下来n行每行两个数x,y,表示在(x,y)处有一头牛。

Output

总共n行,每行一个数ans,第i个数表示第i头牛能到ans个花。

解题报告


考试的时候连暴力都没有打出来,真是十分的失败。
介绍一点部分分的做法:

  1. 如果\(x,y\)比较小的话,那么很显然可以直接暴力dfs
  2. 如果\(n\)比较小的话,可以离散化,然后暴力$O \left( n^3 \right) $
  3. 同样如果要做到\(O \left( n^2 \right)\) ,就需要dp,也就是令\(f[i][j]=f[i][j+1]+f[i+1][j]-f[i+1][j+1]\) ,简单易懂。

考虑优化上述dp,离线+线段树,有这样一个事情, 就是cows只能向下或者向右走,这样对于一个点\((x,y)\),他可能拥有\((x+1,y)\) \((x,y+1)\)无法到达的一些flowers,用类似于差分的想法, 令\(f[i]\)表示当前行\(f[i+1]\)无法到达的花朵,实际上cow\((x,y)\)的答案就是找到下方第一个栅栏(实际上是横向的一条边)\((x_b , y)\),查询当前列\(y\)一个差分的和\((x,x_b)\bf\)

问题更加单纯了,就是转移,详细一点说就是从\(y+1\)\(y\)差分信息怎样变化

  1. 没有栅栏只有花?只需要单点修改下就好
  2. 进入栅栏
    出现一个栅栏? ,也就是进入栅栏的边界,设\((x_l ,x_r)\)是栅栏的上下坐标,很显然,\((x_l,x_r)\)部分的差分应该删掉,并且标记为被覆盖,而\(x_l-1\)这个位置,会获得\((x_l,x_r)\)部分的差分,这点很容易,难以想到的一点是,此时需要询问一下\(cow(x_r+1,y+1)\),并记录这个数值,为3做准备。
    出栅栏
    这个首先也需要做一个区间归零,然后要在\((x_l-1,y)\)的位置减去这个区间加入时做的询问,原因是没有了栅栏的限制,下方的差分会计算两次。这个我感受了好久。

程序的实现

单点修改,区间覆盖(归零),区间查询,从某位置开始第一个障碍点的添加和查询
好难的样子,但好像一个线段树就艹掉了。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int X=1000001,Y=1000000,N=2000001;
struct FEN{
    int xl,xr,y,i;
    bool flag;
    bool operator < (const FEN &o)const{
        return y!=o.y?y>o.y:xl<o.xl;
    }
} fen[N<<1];

struct FLO{
    int x,y;
    bool operator < (const FLO &o)const{
        return y>o.y;
    }
} flo[N];
struct CS{
    int x,y,i;
    bool operator < (const CS &o)const{
        return y>o.y;
    }
} cow[N];
struct SS{
    int nm;
    bool cover,cut;
} seg[X<<2];
int ans[N],fs[N];;
char * cp=(char *)malloc(20000000);
inline void in(int &x){
    for (;*cp<'0'||*cp>'9';cp++);
    for (x=0;*cp>='0'&&*cp<='9';cp++)
        x=x*10+*cp-'0';
}
inline void pushup(int x){
    seg[x].nm=seg[x<<1].nm+seg[x<<1|1].nm;
    seg[x].cut=seg[x<<1].cut|seg[x<<1|1].cut;
}
inline void paint(int x){
    seg[x].cover=1;
    seg[x].nm=0;
}
inline void pushdown(int x){
    if(seg[x].cover){
        paint(x<<1),paint(x<<1|1);
        seg[x].cover=0;
    }
}
void add(int x,int l,int r,int pur,int val){
    seg[x].nm+=val;
    if (l==r) return; 
    int mid=(l+r)>>1;
    pushdown(x);
    if(pur<=mid)
        add(x<<1,l,mid,pur,val);
    else 
        add(x<<1|1,mid+1,r,pur,val);
    pushup(x);
}
void cover(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        paint(x);
        return;
    }
    pushdown(x);
    int mid=(l+r)>>1;
    if(L<=mid)cover(x<<1,l,mid,L,R);
    if(R>mid)cover(x<<1|1,mid+1,r,L,R);
    pushup(x);
}
int query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return seg[x].nm;
    }
    int mid=(l+r)>>1,ans=0;
    pushdown(x);
    if(L<=mid)ans+=query(x<<1,l,mid,L,R);
    if(R>mid)ans+=query(x<<1|1,mid+1,r,L,R);
    return ans;
}
void update(int x,int l,int r,int pur){
    if(l==r){
        seg[x].cut^=1;
        return; 
    }
    int mid=l+r>>1;
    pushdown(x);
    if(pur<=mid)
        update(x<<1,l,mid,pur);
    else 
        update(x<<1|1,mid+1,r,pur);
    pushup(x);
}
int next(int x,int l,int r,int L){
    if(l>=L){
        if(seg[x].cut){
            while(l!=r)
                if(seg[x<<1].cut)
                    x<<=1,r=l+r>>1;
                else 
                    x=x<<1|1,l=(l+r>>1)+1;
            return l;
        }
        else return 0;
    }
    int tmp,mid=(l+r)>>1;
    pushdown(x);
    if(L<=mid&&(tmp=next(x<<1,l,mid,L)))
        return tmp;
    else 
        return next(x<<1|1,mid+1,r,L);
}
int main(){
//  freopen("4422.in","r",stdin);
    fread(cp,1,20000000,stdin);
    int f,m,n,x1,y1,x2,y2;
    in(f);
    for(int i=f;i--;){
        in(x1),in(y1),in(x2),in(y2);
        fen[i<<1]=(FEN){x1,x2,y1-1,i,0};
        fen[i<<1|1]=(FEN){x1,x2,y2,i,1};
    }
    sort(fen,fen+(f<<1));
    in(m);
    for(int i=m;i--;)
        in(flo[i].x),in(flo[i].y);
    sort(flo,flo+m);
    in(n);
    for(int i=0;i<n;++i){
        in(cow[i].x),in(cow[i].y);
        cow[i].i=i;
    }
    sort(cow,cow+n);
    f=m=n=0;
    update(1,1,Y,Y);
    int sum,cut;
    for(int i=Y;i;--i){
        for(;fen[f].y==i;++f)
            if(fen[f].flag==0){
                cover(1,1,Y,fen[f].xl,fen[f].xr);
                if(fen[f].xl!=1)
                    add(1,1,Y,fen[f].xl-1,-fs[fen[f].i]);
                if(fen[f].xl!=1)
                    update(1,1,Y,fen[f].xl-1);
                if(fen[f].xr!=Y)
                    update(1,1,Y,fen[f].xr);
            }else{
                cut=next(1,1,Y,fen[f].xr);
                sum=query(1,1,Y,fen[f].xl,fen[f].xr);
                fs[fen[f].i]=query(1,1,Y,fen[f].xr+1,cut);
                cover(1,1,Y,fen[f].xl,fen[f].xr);
                if(fen[f].xl>1)
                    add(1,1,Y,fen[f].xl-1,sum+fs[fen[f].i]);
                if(fen[f].xl!=1)
                    update(1,1,Y,fen[f].xl-1);
                if(fen[f].xr!=Y)
                    update(1,1,Y,fen[f].xr);
            }
        for(;flo[m].y==i;++m){
            add(1,1,Y,flo[m].x,1);
        }
        for(;cow[n].y==i;++n){
            cut=next(1,1,Y,cow[n].x);
            ans[cow[n].i]=query(1,1,Y,cow[n].x,cut);
        }
    }
    for(int i=0;i<n;++i)
        printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/ShallWe2000/p/5762193.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值