【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】

题意

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

题解

暴力

首先,可以想出一个很显然的dp,设 d p i , j dp_{i,j} dpi,j表示方格 ( i , j ) (i,j) (i,j)能到达的花数。要分类讨论,前三种情况很简单,再次不赘述,特别地,有一种情况不能忽略,如下图:(红色范围为栏杆,蓝色为当前格)
【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】_第1张图片
如果 d p i , j = d p i + 1 , j + d p i , j + 1 dp_{i,j}=dp_{i+1,j}+dp_{i,j+1} dpi,j=dpi+1,j+dpi,j+1,会发现黑色部分重复计算,需要减去围栏左下角的 d p dp dp值。

时间复杂度 O ( n 2 ) O(n^2) O(n2),需要优化

优化

我们发现除了有围栏遮挡的情况外,当前状态的更新都用到 d p i + 1 , j dp_{i+1,j} dpi+1,j d p i , j + 1 dp_{i,j+1} dpi,j+1,可以考虑差分
我们可以按 y y y从大往小扫描线,维护数组 f i f_i fi,表示第y列中(注意题目中的y是横坐标) d p i , y − d p i + 1 , y dp_{i,y}-dp_{i+1,y} dpi,ydpi+1,y的值。可以画个图帮助理解:
【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】_第2张图片
如上图 f 1 f_1 f1统计范围是蓝色部分, f 2 f_2 f2也是它对应的横条, f 3 f_3 f3统计围栏内,而 f 4 f_4 f4则统计黄色部分,一个“7”形。
这样,要统计某一头牛 ( x , y ) (x,y) (x,y)能到达的花,只需找出第一个挡住它的围栏横坐标(R),答案就是 ∑ x ≤ i ≤ R f i \sum_{x\leq i\leq R}f_i xiRfi

现在问题是当扫描完一条线, y y y值减一时,如何维护 f i f_i fi?同样要分类讨论。
1.遇到花,直接在对应位置把 f i f_i fi加一即可
2.遇到牛,更新答案
3.遇到栏杆的开始:设栏杆纵坐标为 y y y,横坐标范围为 l l l r r r。首先需要把 f l − 1 f_{l-1} fl1加上旧的 ∑ l ≤ i ≤ r f i \sum_{l\leq i\leq r}f_i lirfi(即上图黄色图形的下边三格),然后把 f l f_l fl f r f_r fr设为0(因为出不去),再把 l l l r r r标记为围栏(更新答案要用)
4.遇到围栏的结束:设栏杆纵坐标为 y y y,横坐标范围为 l l l r r r。需要把 f l − 1 f_{l-1} fl1减去重复部分(像dp第4种情况一样,即上图黄色图形的下边两格与 f 1 , f 2 f_1,f_2 f1,f2重复,重复部分需要在情况3记录),把 f l f_l fl f r f_r fr设为0(因为进不去围栏),再把 l l l r r r删除围栏标记

综上所述,我们要对 f i f_i fi进行单点修改,区间复制,区间求和,建立标记和查询后一个标记。可以用线段树维护。
(水平有限,表达可能不清,请结合代码)

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define db double
#define ll long long
#define inf 1000005
#define eps 1e-6
#define INF 2005
char buf[10000001],*Hd,*Tl;
#define getchar(ch) {if (Hd==Tl){int len=fread(buf,1,10000000,stdin);Hd=buf,Tl=Hd+len;}ch=(*Hd++);}
//#define getchar(ch) ch=getchar()
#define rd(n) {n=0;char ch;int f=0;do{getchar(ch);if(ch=='-'){f=1;}}while(ch<'0'||ch>'9');while('0'<=ch&&ch<='9'){n=(n<<1)+(n<<3)+ch-48;getchar(ch);}if(f)n=-n;}
using namespace std;

inline void write(int x){
   char c=x%10+48;
   if (x<10){
       putchar(c);
       return;
   }
   write(x/10);
   putchar(c);
   return;
}

struct ST{
   int sum,set,fen;
}t[inf*4];

void ST_pushdown(int u){
   if (t[u].set!=-1){
       t[u*2+1].set=t[u*2].set=0;
       t[u*2+1].sum=t[u*2].sum=0;
       t[u].set=-1;
   }
   return;
}

void ST_pushup(int u){
   t[u].sum=t[u*2].sum+t[u*2+1].sum;
   t[u].fen=(t[u*2].fen|t[u*2+1].fen);
   return;
}

void ST_build(int u,int l,int r,int N){
   t[u].set=-1;
   if (l==r){
       if (l==N){
           t[u].fen=1;
       }
       return;
   }
   int mid=(l+r)/2;
   ST_build(u*2,l,mid,N);
   ST_build(u*2+1,mid+1,r,N);
   ST_pushup(u);
   return;
}

void ST_add(int u,int l,int r,int k,int v){
   if (l==r){
       t[u].sum+=v;
       return;
   }
   ST_pushdown(u);
   int mid=(l+r)/2;
   if (k<=mid){
       ST_add(u*2,l,mid,k,v);
   }
   else{
       ST_add(u*2+1,mid+1,r,k,v);
   }
   ST_pushup(u);
   return;
}

void ST_set0(int u,int l,int r,int L,int R){
   if (L<=l && r<=R){
       t[u].set=0;
       t[u].sum=0;
       return;
   }
   ST_pushdown(u);
   int mid=(l+r)/2;
   if (L<=mid){
       ST_set0(u*2,l,mid,L,R);
   }
   if (R>mid){
       ST_set0(u*2+1,mid+1,r,L,R);
   }
   ST_pushup(u);
   return;
}

void ST_setfen(int u,int l,int r,int k){
   if (l==r){
       t[u].fen^=1;
       return;
   }
   int mid=(l+r)/2;
   ST_pushdown(u);
   if (k<=mid){
       ST_setfen(u*2,l,mid,k);
   }
   else{
       ST_setfen(u*2+1,mid+1,r,k);
   }
   ST_pushup(u);
   return;
}

int ST_nxtfen(int u,int l,int r,int k){
   if (k<=l){
       if (!t[u].fen){
           return 0;
       }
       while (l!=r){
           int mid=(l+r)/2;
           if (t[u*2].fen){
               u=u*2,r=mid;
           }
           else{
               u=u*2+1,l=mid+1;
           }
       }
       return l;
   }
   ST_pushdown(u);
   int mid=(l+r)/2;
   if (k<=mid){
       int ans=ST_nxtfen(u*2,l,mid,k);
       return ans?ans:ST_nxtfen(u*2+1,mid+1,r,k);
   }
   return ST_nxtfen(u*2+1,mid+1,r,k);
}

int ST_query(int u,int l,int r,int L,int R){
   if (L<=l && r<=R){
       return t[u].sum;
   }
   ST_pushdown(u);
   int mid=(l+r)/2,ans=0;
   if (L<=mid){
       ans+=ST_query(u*2,l,mid,L,R);
   }
   if (R>mid){
       ans+=ST_query(u*2+1,mid+1,r,L,R);
   }
   return ans;
}

struct line{
   int l,r,y;
   int id,p;
   line(){}
   line(int x1,int x2,int yy,int ii,int pp){
       l=x1,r=x2,y=yy,id=ii,p=pp;
   }
}L[inf];

bool operator<(line _1,line _2){
   if (_1.y==_2.y){
       return _1.l<_2.l;
   }
   return _1.y>_2.y;
}

struct point{
   int x,y;
   int id;
}C[inf],F[inf];

bool operator<(point _1,point _2){
   return _1.y>_2.y;
}

int lcnt,ccnt,fcnt,N;
int ans[inf],s[inf];

void addline(int id){
   if (L[id].p==0){
       int nxt=ST_nxtfen(1,1,N,L[id].r);
       s[L[id].id]=ST_query(1,1,N,L[id].r+1,nxt);
       int sum=ST_query(1,1,N,L[id].l,L[id].r);
       ST_set0(1,1,N,L[id].l,L[id].r);
       if (L[id].l!=1){
           ST_add(1,1,N,L[id].l-1,sum+s[L[id].id]);
           ST_setfen(1,1,N,L[id].l-1);
       }
       ST_setfen(1,1,N,L[id].r);
   }
   else{
       ST_set0(1,1,N,L[id].l,L[id].r);
       if (L[id].l!=1){
           ST_add(1,1,N,L[id].l-1,-s[L[id].id]);
           ST_setfen(1,1,N,L[id].l-1);
       }
       ST_setfen(1,1,N,L[id].r);
   }
   return;
}

int main(){
   int cnt,x1,x2,y1,y2;
   rd(cnt)
   for (int i=1;i<=cnt;i++){
       rd(x1) rd(y1) rd(x2) rd(y2)
       L[i*2]=line(x1,x2,y2,i,0);
       L[i*2-1]=line(x1,x2,y1-1,i,1);
       N=max(N,x2);
   }
   lcnt=cnt*2;
   rd(fcnt)
   for (int i=1;i<=fcnt;i++){
       rd(F[i].x) rd(F[i].y)
       N=max(N,F[i].x);
   }
   rd(ccnt)
   for (int i=1;i<=ccnt;i++){
       rd(C[i].x) rd(C[i].y) 
       N=max(N,C[i].x);
       C[i].id=i;
   }
   sort(L+1,L+lcnt+1);
   sort(C+1,C+ccnt+1);
   sort(F+1,F+fcnt+1);
   N=inf-4;
   ST_build(1,1,N,N);
   int li=1,ci=1,fi=1;
   for (int i=N;i>=1;i--){
       for (;L[li].y==i;li++){
           addline(li);
       }
       for (;F[fi].y==i;fi++){
           ST_add(1,1,N,F[fi].x,1);
       }
       for (;C[ci].y==i;ci++){
           int nxt=ST_nxtfen(1,1,N,C[ci].x);
           ans[C[ci].id]=ST_query(1,1,N,C[ci].x,nxt);
       }
   }
   for (int i=1;i<=ccnt;i++){
       write(ans[i]);
       putchar('\n');
   }
   return 0;
}

你可能感兴趣的:(【BZOJ4422】Cow Confinement【扫描线】【差分】【线段树】)