一个 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)能到达的花数。要分类讨论,前三种情况很简单,再次不赘述,特别地,有一种情况不能忽略,如下图:(红色范围为栏杆,蓝色为当前格)
如果 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,y−dpi+1,y的值。可以画个图帮助理解:
如上图 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 ∑x≤i≤Rfi
现在问题是当扫描完一条线, 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} fl−1加上旧的 ∑ l ≤ i ≤ r f i \sum_{l\leq i\leq r}f_i ∑l≤i≤rfi(即上图黄色图形的下边三格),然后把 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} fl−1减去重复部分(像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;
}