zoukankan      html  css  js  c++  java
  • HDU6089 *分子(变形线段树)

    题目描述

    n*m的平面内有K个不安全点,Q个询问位置在(x,y)的人能走到多少个点?从(x,y)走到(x',y')是合法的,当且仅当(x,y)和(x',y')之间的矩形中不包含不安全点。

    题解

    问题相当于平面中有若干障碍点,询问以某一个点为四个角之一的不包含障碍点的矩形有多少个。

    我们只需要考虑一个方向,接下来把整个图旋转90度再算即可 

    那一个方向怎么求呢?

    正难则反,我们可以考虑逆向思考

    如图,线与线交点表示一个坐标,黑点表示不安全点,白点表示询问点

    白点右下方可以走到的点数=蓝线内的点数-阴影内的点数

    那阴影到底是什么呢?

    它其实就是 每一高度的最右边的黑点向下作垂线,与坐标轴围成的最大区域

    换句话说,它满足 如果上方的右边界在下方的原右边界右边, 则下方的右边界按上方的算

    那我们可以以高度划定区间建一颗线段树,做一些特殊处理就可以求出阴影了
     
    原谅我表达能力有限,具体看代码吧
    //正难则反 
    #include<bits/stdc++.h>
    #define mid ((l+r)>>1)
    using namespace std;
    const int N=100005;
    typedef long long ll;
    int n,m,K,Q,T_Max[N<<2],tot,Max;
    //T_Max表y在某一区间内的x的最大值
    ll ans[N],T_sum[N<<2],T_suml[N<<2];
    struct node {
        int x,y,id,pd;
        node() {} 
        node(int a,int b,int c,int d) {
            x=a;y=b;id=c;pd=d;
        }
    }a[N*2],tr[N],q[N];
    bool cmp(const node &A,const node &B){
        return A.x<B.x||(A.x==B.x&&A.y<B.y)||(A.x==B.x&&A.y==B.y&&A.pd<B.pd);
    }
    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<<1)+(x<<3)+ch-'0',ch=getchar();
        return x*f;
    }
    ll query_sum(int u,int l,int r,int a,int b,int &mx){
        if (a<=l&&r<=b){
            if(mx>=T_Max[u]) return (ll)mx*(r-l+1);
            if(l==r) return mx=T_Max[u];
            int tt=T_Max[u<<1|1];
            ll res=0;
            if(mx>=tt){//即T_Max[u<<1|1]<=mx<T_Max[u<<1] 
                res+=(ll)mx*(r-mid);
                res+=query_sum(u<<1,l,mid,a,b,mx);
            }else{//即mx<T_Max[u<<1|1](T_Max[u<<1]的范围不限) 
                res+=T_suml[u];
                res+=query_sum(u<<1|1,mid+1,r,a,b,mx);
            }
            mx=T_Max[u];
            return res;
        }
        ll s=0;
        if(b>mid) s+=query_sum(u<<1|1,mid+1,r,a,b,mx);//先更新上边以更新右边界 
        if(a<=mid) s+=query_sum(u<<1,l,mid,a,b,mx);
        return s;
    }
    void ins(int u,int l,int r,int y,int x){
        if(l==r){
            if(x>T_Max[u]) T_Max[u]=T_sum[u]=x;
            return;
        }
        if(y<=mid) ins(u<<1,l,mid,y,x);
        else ins(u<<1|1,mid+1,r,y,x);
        int tt=T_Max[u<<1|1];
        T_suml[u]=query_sum(u<<1,l,mid,l,mid,tt);//注意直接写T_max[u<<1|1]的话可能会被修改
        T_Max[u]=max(T_Max[u<<1],T_Max[u<<1|1]);
        T_sum[u]=T_suml[u]+T_sum[u<<1|1];
        //如果T_max[u<<1|1]>[l,mid]区间的原右边界,则[l,mid]区间的右边界按T_max[u<<1|1]算 
        //否则,按原右边界算
    }
    void calc(){
        tot=0;
        for (int i=1;i<=K;i++) a[++tot]=node(tr[i].x,tr[i].y,i,0);
        for (int i=1;i<=Q;i++) a[++tot]=node(q[i].x,q[i].y,i,1);
        sort(a+1,a+tot+1,cmp);
        for (int i=1;i<=tot;i++){
            if(!a[i].pd) ins(1,1,m,a[i].y,a[i].x);
            else{
                Max=0;ans[a[i].id]+=query_sum(1,1,m,1,a[i].y,Max);//二维数点 
                Max=0;ans[a[i].id]-=query_sum(1,1,m,a[i].y,a[i].y,Max);//减去重复的同行/同列轴 
            }
        }
    }
    int main(){
        n=read();m=read();K=read();Q=read();
        for (int i=1;i<=K;i++) tr[i].x=read(),tr[i].y=read();
        for (int i=1;i<=Q;i++) q[i].x=read(),q[i].y=read(),ans[i]=0;
        for (int i=0;i<4;i++){
            calc();
            for (int i=1;i<=(m<<2); i++) T_sum[i]=T_suml[i]=T_Max[i]=0;//清零
            for (int j=1;j<=K;j++) tr[j].x=n-tr[j].x+1,swap(tr[j].x,tr[j].y);//旋转90度 
            for (int j=1;j<=Q;j++) q[j].x=n-q[j].x+1,swap(q[j].x,q[j].y);
            swap(n,m);
        }
        for (int i=1;i<=Q;i++) printf("%lld
    ",(ll)n*m-ans[i]);
        return 0;
    }
  • 相关阅读:
    Redis 数据类型
    Redis 配置
    Redis 安装
    Redis 简介
    MongoDB 自动增长
    MongoDB 固定集合
    IDEA安装
    云市场 > 软件服务 > 建站系统 > 建站模板-官网企业套餐建站模板-官网企业套餐
    腾讯云SSL证书管理
    4-1 创建项目,并了解项目目录下部分文件的作用
  • 原文地址:https://www.cnblogs.com/HarryPotter-fan/p/11317080.html
Copyright © 2011-2022 走看看