牛客国庆day3(2018hncpc) E. Grid 线段树

 

Grid

 

这是跟平时不太一样的线段树。

 

这道题要用是要线段树记录区间(线段),然后判断  当前所记录的  所有线段  总共覆盖的长度,也就是说,线段可能会重叠,重叠的区间长度不能重复计算,要计算实际得到的  所有线段的长度。

 

想想 怎么记录? 跟平时一样的话,我大概就是,把线段分为单个单个点,然后把区间标记为1,然后计算的时候就,到了标记为1的区间,就直接ans+=r-l+1,但是,因为是分散的区间线段,搜索时间会大于平时。有点不足,也不知道有没有别的做法。

但是,这道题还不能用这种做法,因为区间长度是1e9,需要离散。

 

所以看了别人的做法:离散询问出现的点的坐标,然后,线段是[l,r],把线段树的[l,r]的值置为h[r]-h[l],这个也不是我想说的重点。

但是,离散了之后,mid和mid+1的值就断开了,这样子 两个区间的值不就不连续了吗?会缺少[h[mid],h[mid+1]]的值。

 

所以看了别人的做法:线段树的左右子树的区间是[l,mid] [mid,r],或者  更准确的说是,传递到左右子树的离散数组区间是[ h[l],h[mid] ]和[ h[mid],h[r] ] ,这样子就能解决~...了吗?有个小细节忘了说 线段区间[val[l],val[r]]的长度并不是真实的h[r]-h[l],在记录r的时候 h[r]的值实际是val[r+1],这样才满足区间的真实长度(r-l+1)嘛。

 

想无,记下来。

 

代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define debug printf("!");
using namespace std;
typedef long long ll;
const int maxn=1e5+50;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;

struct P{
    int ly;ll sum;
}tree1[maxn<<3],tree2[maxn<<3];

int h[maxn<<1],tot,nn,mm;
int val(int v)
{
    return lower_bound(h+1,h+1+tot,v)-h;
}


void down(int k,int L,int R,P tree[])
{
    int mid=(L+R)>>1;
    tree[k<<1].sum=h[mid]-h[L];
    tree[k<<1|1].sum=h[R]-h[mid];
    tree[k<<1].ly=1;
    tree[k<<1|1].ly=1;
    tree[k].ly=0;
}

void update(int k,int L,int R,int l,int r,P tree[])
{
    if(l==L&&R==r)
    {
        tree[k].sum=h[r]-h[l];
        tree[k].ly=1;
        return ;
    }
    if(tree[k].ly)down(k,L,R,tree);
    int mid=(L+R)>>1;
    if(r<=mid)update(k<<1,L,mid,l,r,tree);
    else if(l>=mid)update(k<<1|1,mid,R,l,r,tree);
    else
    {
        update(k<<1,L,mid,l,mid,tree);
        update(k<<1|1,mid,R,mid,r,tree);
    }
    tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

struct Q{
    int op,a,b;
}p[maxn];

int main()
{
    ll n,m;
    ll ans,t;
    int q,op,a,b;
    while(~scanf("%lld%lld%d",&n,&m,&q))
    {
        tot=0;
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d%d",&p[i].op,&p[i].a,&p[i].b);
            h[++tot]=p[i].a;h[++tot]=p[i].b+1;
        }
        h[++tot]=n+1;h[++tot]=m+1;
        sort(h+1,h+1+tot);
        tot=unique(h+1,h+1+tot)-h-1;
        nn=val(n+1);mm=val(m+1);
        for(int i=1;i<=tot*4;i++)
        {
            tree1[i].sum=tree1[i].ly=0;
            tree2[i].sum=tree2[i].ly=0;
        }
        for(int i=1;i<=q;i++)
        {
            a=val(p[i].a);b=val(p[i].b+1);
            if(p[i].op==1)update(1,1,nn,a,b,tree1);
            else update(1,1,mm,a,b,tree2);
            ans=(n*m)-(tree1[1].sum*m+tree2[1].sum*n)+(tree1[1].sum*tree2[1].sum);
            if(tree1[1].sum==0)ans+=tree2[1].sum;
            else if(tree2[1].sum==0)ans+=tree1[1].sum;
            else ans++;
            printf("%lld\n",ans);
        }
    }
}

 

 

但是!!!但是,更好的做法是:动态开点线段树

但是!!!但是,在牛客这样做会超内存。等国庆过后去别的oj补题。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define debug printf("!");
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;

int sum1[maxn<<3],L1[maxn<<3],R1[maxn<<3],cnt1,T1;
int sum2[maxn<<3],L2[maxn<<3],R2[maxn<<3],cnt2,T2;

void init()
{
    for(int i=0;i<=cnt1;i++)sum1[i]=L1[i]=R1[i]=0;
    for(int i=0;i<=cnt2;i++)sum2[i]=L2[i]=R2[i]=0;
    cnt1=cnt2=0;
}

void update1(int &rt,int l,int r,int tl,int tr)
{
    if(!rt)rt=++cnt1;
    if(sum1[rt]==r-l+1)return;
    if(tl<=l&&r<=tr)
    {
        sum1[rt]=r-l+1;return;
    }
    int mid=(l+r)>>1;
    if(tl<=mid)update1(L1[rt],l,mid,tl,tr);
    if(tr>mid)update1(R1[rt],mid+1,r,tl,tr);
    sum1[rt]=sum1[L1[rt]]+sum1[R1[rt]];
}
void update2(int &rt,int l,int r,int tl,int tr)
{
    if(!rt)rt=++cnt2;
    if(sum2[rt]==r-l+1)return;
    if(tr<=l&&r<=tr)
    {
        sum2[rt]=r-l+1;return;
    }
    int mid=(l+r)>>1;
    if(tl<=mid)update2(L2[rt],l,mid,tl,tr);
    if(tr>mid)update2(R2[rt],mid+1,r,tl,tr);
    sum2[rt]=sum2[L2[rt]]+sum2[R2[rt]];
}
int main()
{
    ll n,m;
    ll ans,t;
    int q,op,a,b;
    while(~scanf("%lld%lld%d",&n,&m,&q))
    {
        init();
        while(q--)
        {
            scanf("%d%d%d",&op,&a,&b);
            if(op==1)update1(T1,1,n,a,b);
            else update2(T2,1,m,a,b);
            ans=n*m-((ll)sum1[1]*m+(ll)sum2[1]*n)+(ll)sum1[1]*(ll)sum2[1];
            if(sum1[1]==0)ans+=sum2[1];
            else if(sum2[1]==0)ans+=sum1[1];
            else ans++;
            printf("%lld\n",ans);
        }
    }
}

 

转载于:https://www.cnblogs.com/kkkek/p/11621399.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值