P4348 / CF101480C Cow Confinement 题解
题目描述
一张 \(10^6\times 10^6\) 的网格图中,有 \(n\) 头牛和 \(m\) 朵花。
还有 \(f\) 个矩形围栏,保证围栏互不相交(没有公共点和公共边)。
已知牛只能往右或往下走,并且不能经过围栏,对每头牛,计算有多少朵花可以被它走到。
数据范围
- \(1\le n\le 2\cdot 10^5,0\le m,f\le 2\cdot 10^5\) 。
- 对牛和花的位置 \((r,c)\) ,有 \(1\le r,c\le 10^6\) 。
- 记矩形围栏内左上角格子坐标 \((r_1,c_1)\) ,右下角格子坐标 \((r_2,c_2)\) ,有 \(1\le r_1\le r_2\le 10^6,1\le c_1\le c_2\le 10^6\) 。
时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
带特殊条件的二维数点,从右往左扫描线。
假设当时扫到第 \(j\) 列,线段树维护对每个高度 \(i\) ,第一步往右走的答案。
-
加入右边界 \([l,r]\) 。
在 \(x\) 的位置加上区间 \([l,r]\) 的答案(也就是绿色部分),然后将 \([l,r]\) 的答案清零。
这里还要记录 \(y\) 的答案,原因后面会讲。
-
删除左边界 \([l,r]\) 。
我们需要更新 \(x\) 点答案,由于 \(v\) 区域在扫描右边界时已经被计算,所以增量为 \(u\) 点答案减去 \(v\) 点答案。
\(v\) 点贡献刚好是扫到右边界时记录的内容。
当然,区间 \([l,r]\) 不能往右走,区间清零。
-
加入一朵花,单点 \(+1\) 即可。
-
询问一头牛,二分找到往下走能到达的最远位置,然后区间求和。
用
multiset存储所有 \(l-1,r+1\) 的值,查询最远位置即查询后继。
注意顺序不能乱,必须先更新边界,再统计花朵贡献,最后回答询问。
另外由于我们需要记录右边界下一格的答案,所以线段树值域要开到 \(10^6+1\) 。
时间复杂度 \(\mathcal O((n+m+f)\log V)\) 。
#include<bits/stdc++.h>
using namespace std;
const int lim=1e6+1,maxn=1e6+5;
int m,n,q;
int res[maxn],tmp[maxn];
int cov[4*maxn],sum[4*maxn];
multiset<int> s;
struct oper
{
int l,r,id,op;
};
vector<oper> vec[maxn];
bool cmp(oper a,oper b)
{
if(a.op!=b.op) return a.op<b.op;
return a.l<b.l;
}
int read()
{
int q=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) q=10*q+ch-'0',ch=getchar();
return q;
}
void pushcov(int p)
{
cov[p]=1,sum[p]=0;
}
void pushdown(int p)
{
if(!cov[p]) return ;
pushcov(2*p),pushcov(2*p+1),cov[p]=0;
}
void pushup(int p)
{
sum[p]=sum[2*p]+sum[2*p+1];
}
void modify(int p,int l,int r,int pos,int val)
{
if(l==r) return sum[p]+=val,void();
int mid=(l+r)/2;
pushdown(p);
if(pos<=mid) modify(2*p,l,mid,pos,val);
else modify(2*p+1,mid+1,r,pos,val);
pushup(p);
}
void update(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return pushcov(p);
if(L>r||R<l) return ;
int mid=(l+r)/2;
pushdown(p);
update(2*p,l,mid,L,R);
update(2*p+1,mid+1,r,L,R);
pushup(p);
}
int query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return sum[p];
if(L>r||R<l) return 0;
int mid=(l+r)/2;
pushdown(p);
return query(2*p,l,mid,L,R)+query(2*p+1,mid+1,r,L,R);
}
int main()
{
q=read();
for(int i=1;i<=q;i++)
{
int l1=read(),r1=read(),l2=read(),r2=read();
vec[r2].push_back({l1,l2,i,1});
vec[r1-1].push_back({l1,l2,i,2});
}
m=read();
while(m--)
{
int x=read(),y=read();
vec[y].push_back({x,0,0,3});
}
n=read();
for(int i=1;i<=n;i++)
{
int x=read(),y=read();
vec[y].push_back({x,0,i,4});
}
s.insert(lim);
for(int i=lim;i;i--)
{
sort(vec[i].begin(),vec[i].end(),cmp);
for(auto p:vec[i])
{
int l=p.l,r=p.r,id=p.id,op=p.op;
if(op==1)
{
modify(1,0,lim,l-1,query(1,0,lim,l,*s.lower_bound(l)));
update(1,0,lim,l,r);
tmp[id]=query(1,0,lim,r+1,*s.lower_bound(r+1));
s.insert(l-1),s.insert(r);
}
if(op==2)
{
modify(1,0,lim,l-1,-tmp[id]);
update(1,0,lim,l,r);
s.erase(s.find(l-1)),s.erase(s.find(r));
}
if(op==3) modify(1,0,lim,l,1);
if(op==4) res[id]=query(1,0,lim,l,*s.lower_bound(l));
}
}
for(int i=1;i<=n;i++) printf("%d\n",res[i]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16533427.html
浙公网安备 33010602011771号