peiwenjun's blog 没有知识的荒原

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]\) 。

    image

    在 \(x\) 的位置加上区间 \([l,r]\) 的答案(也就是绿色部分),然后将 \([l,r]\) 的答案清零。

    这里还要记录 \(y\) 的答案,原因后面会讲。

  • 删除左边界 \([l,r]\) 。

    image

    我们需要更新 \(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;
}

posted on 2022-07-29 20:08  peiwenjun  阅读(11)  评论(0)    收藏  举报

导航