【BZOJ-4422】Cow Confinement 线段树 + 扫描线 + 差分 (优化DP)
4422: [Cerc2015]Cow Confinement
Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 61 Solved: 26
[Submit][Status][Discuss]
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个花。
Sample Input
4
2 2 8 4
1 9 4 10
6 7 9 9
3 3 7 3
9
3 4
8 4
11 5
10 7
10 8
9 8
2 8
4 11
9 11
8
1 1
5 10
6 9
3 7
7 1
4 2
7 5
3 3
2 2 8 4
1 9 4 10
6 7 9 9
3 3 7 3
9
3 4
8 4
11 5
10 7
10 8
9 8
2 8
4 11
9 11
8
1 1
5 10
6 9
3 7
7 1
4 2
7 5
3 3
Sample Output
5
1
0
1
3
1
3
0
1
0
1
3
1
3
0
HINT
0<=f<=200000
0<=m<=200000
1<=n<=200000
Source
Solution
一道idea非常好的题
首先这题可以DP,不过转移之类的需要讨论,比较麻烦
$dp[i][j]=\begin{Bmatrix} 0(下面和右面都有栅栏)\\ dp[i+1][j](右面有栅栏)\\ dp[i][j+1](下面有栅栏)\\ dp[i+1][j]+dp[i][j+1]-dp[i+1][j+1]((i+1,j+1)不是栅栏的左上角)\\ dp[i+1][j]+dp[i][j+1]-dp[x+1][y+1]((i+1,j+1)是左上角,(x,y)是右下角)& & \end{Bmatrix}+flower[i][j]$
这样DP的时间复杂度是$O((N+F+M)^{2})$的
那么考虑优化这个DP
差分出$f[i][j]$表示$(i,j)$能得到,但$(i,j-1)$不能得到的花的数量
那么考虑扫描线,从右往左
那讨论一下各种情况,
遇到花单点数值+1;遇到一个未出现过的栅栏,区间查询和,单点修改数值,区间修改覆盖0;删除一个栅栏,区间修改覆盖0;遇见一头牛,找到下方第一个栅栏,区间求和;
显然都可以用线段树维护
查找下方第一个栅栏?维护一个最小即可
时间复杂度是$O(10^{6}log10^{6})$
实现起来有一些细节,线段树中需要加特判,当所需要修改的坐标为0啊之类的
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXY 1000000 #define MAXF 200010 #define MAXN 200010 #define MAXM 200010 int F,M,N; struct FenceNode{int x1,x2,y1,y2;}fen[MAXF]; struct CowNode{int x,y,id;}cow[MAXN]; struct FlowerNode{int x,y;}flo[MAXM]; struct LineNode{int y,x1,x2,id,f;}line[MAXF<<1]; int tp; struct SegmentTreeNode{int l,r,tag,bk,sum;}tree[MAXY<<2]; int tmp[MAXF],ans[MAXN]; inline void Update(int now) { tree[now].bk=min(tree[now<<1].bk,tree[now<<1|1].bk); tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum; } inline void PushDown(int now) { if (!tree[now].tag || tree[now].l==tree[now].r) return; tree[now].tag=0; tree[now<<1].sum=0; tree[now<<1|1].sum=0; tree[now<<1].tag=1; tree[now<<1|1].tag=1; } inline void BuildTree(int now,int l,int r) { tree[now].l=l,tree[now].r=r; tree[now].sum=0; tree[now].tag=0; tree[now].bk=MAXY; if (l==r) return; int mid=(l+r)>>1; BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r); Update(now); } inline void PointChangeSum(int now,int pos,int D) { if (pos==MAXY+1 || pos==0) return; int l=tree[now].l,r=tree[now].r; PushDown(now); if (l==r) {tree[now].sum+=D; return;} int mid=(l+r)>>1; if (pos<=mid) PointChangeSum(now<<1,pos,D); if (pos>mid) PointChangeSum(now<<1|1,pos,D); Update(now); } inline void PointChangeBreak(int now,int pos,int D) { if (pos==MAXY+1 || pos==0) return; int l=tree[now].l,r=tree[now].r; PushDown(now); if (l==r) {tree[now].bk=D? l:MAXY; return;} int mid=(l+r)>>1; if (pos<=mid) PointChangeBreak(now<<1,pos,D); if (pos>mid) PointChangeBreak(now<<1|1,pos,D); Update(now); } inline void IntervalChange(int now,int L,int R) { if (L==0 || R==MAXY+1 || R<L) return; int l=tree[now].l,r=tree[now].r; PushDown(now); if (L<=l && R>=r) {tree[now].tag=1; tree[now].sum=0; return;} int mid=(l+r)>>1; if (L<=mid) IntervalChange(now<<1,L,R); if (R>mid) IntervalChange(now<<1|1,L,R); Update(now); } inline int IntervalQuerySum(int now,int L,int R) { if (L==0 || R==MAXY+1 || R<L) return 0; int l=tree[now].l,r=tree[now].r; PushDown(now); if (L<=l && R>=r) return tree[now].sum; int mid=(l+r)>>1,re=0; if (L<=mid) re+=IntervalQuerySum(now<<1,L,R); if (R>mid) re+=IntervalQuerySum(now<<1|1,L,R); return re; } inline int IntervalQueryBreak(int now,int L,int R) { if (L==0 || R==MAXY+1 || R<L) return 0; int l=tree[now].l,r=tree[now].r; PushDown(now); if (L<=l && R>=r) return tree[now].bk; int mid=(l+r)>>1,re=MAXY; if (L<=mid) re=min(re,IntervalQueryBreak(now<<1,L,R)); if (R>mid) re=min(re,IntervalQueryBreak(now<<1|1,L,R)); return re; } inline bool cmpLine(LineNode A,LineNode B) {return A.y!=B.y? A.y>B.y : A.x1<B.x1;} inline bool cmpCow(CowNode A,CowNode B) {return A.y>B.y;} inline bool cmpFlower(FlowerNode A,FlowerNode B) {return A.y>B.y;} int main() { F=read(); for (int i=1; i<=F; i++) fen[i].x1=read(),fen[i].y1=read(),fen[i].x2=read(),fen[i].y2=read(); for (int i=1; i<=F; i++) line[++tp]=(LineNode){fen[i].y1-1,fen[i].x1,fen[i].x2,i,-1}, line[++tp]=(LineNode){fen[i].y2,fen[i].x1,fen[i].x2,i,1}; sort(line+1,line+tp+1,cmpLine); M=read(); for (int i=1; i<=M; i++) flo[i].x=read(),flo[i].y=read(); sort(flo+1,flo+M+1,cmpFlower); N=read(); for (int i=1; i<=N; i++) cow[i].x=read(),cow[i].y=read(),cow[i].id=i; sort(cow+1,cow+N+1,cmpCow); BuildTree(1,1,MAXY); int nowl=1,nowf=1,nowc=1,next,sum; for (int i=MAXY; i; i--) { while (line[nowl].y==i) { if (line[nowl].f==-1) { IntervalChange(1,line[nowl].x1,line[nowl].x2); PointChangeSum(1,line[nowl].x1-1,-tmp[line[nowl].id]); PointChangeBreak(1,line[nowl].x1-1,0); PointChangeBreak(1,line[nowl].x2,0); } else { next=IntervalQueryBreak(1,line[nowl].x2,MAXY); sum=IntervalQuerySum(1,line[nowl].x1,line[nowl].x2); tmp[line[nowl].id]=IntervalQuerySum(1,line[nowl].x2+1,next); IntervalChange(1,line[nowl].x1,line[nowl].x2); PointChangeSum(1,line[nowl].x1-1,sum+tmp[line[nowl].id]); PointChangeBreak(1,line[nowl].x1-1,1); PointChangeBreak(1,line[nowl].x2,1); } nowl++; } while (flo[nowf].y==i) PointChangeSum(1,flo[nowf].x,1),nowf++; while (cow[nowc].y==i) next=IntervalQueryBreak(1,cow[nowc].x,MAXY), ans[cow[nowc].id]=IntervalQuerySum(1,cow[nowc].x,next), nowc++; } for (int i=1; i<=N; i++) printf("%d\n",ans[i]); return 0; }
#include<cstdio> #include<iostream> using namespace std; #include<cstring> #include<cmath> #include<algorithm> const int X=1e6+5,Y=1e6,F=2e5+5,M=2e5+5,N=2e5+5; char * cp=(char *)malloc(20000000); void in(int &x){ while(*cp<'0'||*cp>'9')++cp; for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0'); } struct FenS{ int xl,xr; int y; int i; bool flag; bool operator < (const FenS & o)const{ return y!=o.y?y>o.y:xl<o.xl; } }fence[F<<1]; int fsum[F]; struct FloS{ int x,y; bool operator < (const FloS & o)const{ return y>o.y; } }flower[M]; struct CS{ int x,y; int i; bool operator < (const CS & o)const{ return y>o.y; } }cow[N]; int ans[N]; struct SS{ int cnt; bool cover; bool barrier; }segt[X<<2]; #define lson node<<1,l,l+r>>1 #define rson node<<1|1,(l+r>>1)+1,r void out(int node,int l,int r){ printf("segt[%d][%d,%d]={cnt=%d,cover=%d,barrier=%d}\n",node,l,r,segt[node].cnt,segt[node].cover,segt[node].barrier); } void pushup(int node){ segt[node].cnt=segt[node<<1].cnt+segt[node<<1|1].cnt; segt[node].barrier=segt[node<<1].barrier|segt[node<<1|1].barrier; } void paint(int node){ //printf("paint(%d)\n",node); segt[node].cover=1; segt[node].cnt=0; } void pushdown(int node){ if(segt[node].cover){ paint(node<<1),paint(node<<1|1); segt[node].cover=0; } } void add(int node,int l,int r,int x,int delta){ //printf("add(%d,%d,%d,%d,%d)\n",node,l,r,x,delta); //out(node,l,r); segt[node].cnt+=delta; if(l!=r){ pushdown(node); if(x<=l+r>>1)add(lson,x,delta); else add(rson,x,delta); pushup(node); } //printf("add:"); //out(node,l,r); } void cover(int node,int l,int r,int L,int R){ //out(node,l,r); if(L<=l&&r<=R)paint(node); else{ pushdown(node); if(L<=l+r>>1)cover(lson,L,R); if(R>l+r>>1)cover(rson,L,R); pushup(node); } } int query_cnt(int node,int l,int r,int L,int R){ //printf("query(%d,%d,%d,%d,%d)\n",node,l,r,L,R); if(L<=l&&r<=R){ //printf("query_cnt get [%d][%d,%d]=%d\n",node,l,r,segt[node].cnt); return segt[node].cnt; } else{ pushdown(node); int ans=0; if(L<=l+r>>1)ans+=query_cnt(lson,L,R); if(R>l+r>>1)ans+=query_cnt(rson,L,R); return ans; } } void update(int node,int l,int r,int x){ if(l==r)segt[node].barrier^=1; else{ pushdown(node); if(x<=l+r>>1)update(lson,x); else update(rson,x); pushup(node); } //printf("update:"); //out(node,l,r); } int query_barrier(int node,int l,int r,int L){ if(l>=L){ //printf("Query_barrier Get "); //out(node,l,r); if(segt[node].barrier){ while(l!=r) if(segt[node<<1].barrier)node<<=1,r=l+r>>1; else node=node<<1|1,l=(l+r>>1)+1; return l; } else return 0; } else{ int tmp; pushdown(node); if(L<=l+r>>1&&(tmp=query_barrier(lson,L)))return tmp; else return query_barrier(rson,L); } } int main(){ freopen("cow.in","r",stdin); freopen("cow.out","w",stdout); fread(cp,1,20000000,stdin); int f; in(f); int x1,y1,x2,y2; for(int i=f;i--;){ in(x1),in(y1),in(x2),in(y2); fence[i<<1]=(FenS){x1,x2,y1-1,i,0}; fence[i<<1|1]=(FenS){x1,x2,y2,i,1}; } sort(fence,fence+(f<<1)); int m; in(m); for(int i=m;i--;)in(flower[i].x),in(flower[i].y); sort(flower,flower+m); int n; 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,br; for(int i=Y;i;--i){ //printf("----%d----\n",i); for(;fence[f].y==i;++f) if(fence[f].flag==0){ cover(1,1,Y,fence[f].xl,fence[f].xr); if(fence[f].xl!=1)add(1,1,Y,fence[f].xl-1,-fsum[fence[f].i]); if(fence[f].xl!=1)update(1,1,Y,fence[f].xl-1); if(fence[f].xr!=Y)update(1,1,Y,fence[f].xr); } else{ br=query_barrier(1,1,Y,fence[f].xr); sum=query_cnt(1,1,Y,fence[f].xl,fence[f].xr); fsum[fence[f].i]=query_cnt(1,1,Y,fence[f].xr+1,br); cover(1,1,Y,fence[f].xl,fence[f].xr); if(fence[f].xl>1)add(1,1,Y,fence[f].xl-1,sum+fsum[fence[f].i]); if(fence[f].xl!=1)update(1,1,Y,fence[f].xl-1); if(fence[f].xr!=Y)update(1,1,Y,fence[f].xr); } for(;flower[m].y==i;++m){ //cout<<flower[m].x<<","<<flower[m].y<<endl; add(1,1,Y,flower[m].x,1); } for(;cow[n].y==i;++n){ br=query_barrier(1,1,Y,cow[n].x); //printf("br(%d)=%d\n",cow[n].x,br); ans[cow[n].i]=query_cnt(1,1,Y,cow[n].x,br); //printf("query(%d,%d)=%d\n",cow[n].x,br,ans[cow[n].i]); } } for(int i=0;i<n;++i)printf("%d\n",ans[i]); }
TA爷的模拟赛.....暴力都没写.....有点对不起TA爷....(不过似乎当时这题没得分的?)
自己的代码比较丑,这里附上当时TA爷的std,供参考
——It's a lonely path. Don't make it any lonelier than it has to be.