校门外的树

描述

校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
K=1,K=1,读入l、r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同
K=2,读入l,r表示询问l~r之间能见到多少种树
(l,r>0)

格式

输入格式

第一行n,m表示道路总长为n,共有m个操作
接下来m行为m个操作

输出格式

对于每个k=2输出一个答案

样例1

样例输入1

5 4
1 1 3
2 2 5
1 2 4
2 3 5

样例输出1

1
2

限制

1s

提示

范围:20%的数据保证,n,m<=100
60%的数据保证,n <=1000,m<=50000
100%的数据保证,n,m<=50000

来源

dejiyu@CSC WorkGroup

题目链接:https://vijos.org/p/1448

分析:这题目从上午九点写到下午四点,历经七个小时的磨难,只为给大家提供最优质的方法!

这道题我用了三种方法去解决!

第一种:线段树【时间花费最长,也最伤脑的写法】,做法是将[a,b]种上一种树,这个修改操作影响的询问满足,

询问区间与[a,b]有交,转化为统计总修改数-与某询问交为空集的修改数

对于一个修改操作[l,r],与它为空集的询问[a,b]满足a∈[1,l-1]或者b∈[r+1,n]

用两棵线段树维护,修改[l,r],将第一棵的[1,l-1]区间+1,第二棵[r+1,n]区间+1

询问[a,b],答案为之前的修改数-(第一棵单点询问b+第二棵单点询问a)

代码中线段树结点的l,r其实就是两棵线段树。。。标记永久化

下面给出线段树的代码:

 #include <bits/stdc++.h>
using namespace std;
const int N=;
int n,m;
inline int read()
{
int x=,f=;
char ch=getchar();
while(ch<''||ch>'')
{
if(ch=='-')
f=-;
ch=getchar();
}
while(ch>=''&&ch<='')
{
x=x*+ch-'';
ch=getchar();
}
return x*f;
}
inline void write(int x)
{
if(x<)
{
putchar('-');
x=-x;
}
if(x>)
{
write(x/);
}
putchar(x%+'');
}
struct Tree
{
int l,r;
int left,right;
}tree[N<<];
inline void buildtree(int x,int y,int pos)
{
tree[pos].left=x;
tree[pos].right=y;
if(x==y)
{
return;
}
int mid=(x+y)/;
buildtree(x,mid,pos*);
buildtree(mid+,y,pos*+);
}
inline void insertl(int x,int y,int pos)
{
int l=tree[pos].left;
int r=tree[pos].right;
if(l==x&&r==y)
{
tree[pos].l++;
return;
}
int mid=(l+r)/;
if(y<=mid)
insertl(x,y,pos*);
else if(x>mid)
insertl(x,y,pos*+);
else
{
insertl(x,mid,pos*);
insertl(mid+,y,pos*+);
}
}
inline void insertr(int x,int y,int pos)
{
int l=tree[pos].left;
int r=tree[pos].right;
if(l==x&&r==y)
{
tree[pos].r++;
return;
}
int mid=(l+r)/;
if(y<=mid)
insertr(x,y,pos*);
else if(x>mid)
insertr(x,y,pos*+);
else
{
insertr(x,mid,pos*);
insertr(mid+,y,pos*+);
}
}
inline int askl(int k,int x)
{
int l=tree[k].left;
int r=tree[k].right;
if(l==r)
return tree[k].l;
int mid=(l+r)/;
if(x<=mid)
return tree[k].l+askl(k*,x);
else return tree[k].l+askl(k*+,x);
}
inline int askr(int k,int x)
{
int l=tree[k].left;
int r=tree[k].right;
if(l==r)
return tree[k].r;
int mid=(l+r)/;
if(x<=mid)
return tree[k].r+askr(k*,x);
else return tree[k].r+askr(k*+,x);
}
int main()
{
n=read();
m=read();
int tot=;
buildtree(,n,);
for(int i=;i<=m;i++)
{
int t,a,b;
cin>>t>>a>>b;
if(t==)
{
insertl(,a-,);
insertr(b+,n,);
tot++;
}
else
{
int ans=askr(,a)+askl(,b);
write(tot-ans);
cout<<endl;
}
}
return ;
}

第二种写法:树状数组

做法:这题是一条条线段,所以我们可以用线段树之类的东东来实现,然后感觉树状数组写起来简单一点所以就打了
开两个数组来存一个是开始的点的数量,一个是结束的 ,然后随便搞一下,最后输出就可以了

下面给出树状数组写法:

 #include <bits/stdc++.h>
using namespace std;
const int N=;
int l[N],r[N];
int n,m;
inline int read()
{
int x=,f=;
char ch=getchar();
while(ch<''||ch>'')
{
if(ch=='-')
f=-;
ch=getchar();
}
while(ch>=''&&ch<='')
{
x=x*+ch-'';
ch=getchar();
}
return x*f;
}
inline void write(int x)
{
if(x<)
{
putchar('-');
x=-x;
}
if(x>)
{
write(x/);
}
putchar(x%+'');
}
int lowbit(int x)
{
return x&-x;
}
void add(int x,int d,int c[])
{
while(x<=n)
{
c[x]+=d;
x+=lowbit(x);
}
}
int sum(int x,int c[])
{
int s=;
while(x>)
{
s+=c[x];
x-=lowbit(x);
}
return s;
}
int main()
{
int k,x,y;
n=read();
m=read();
for(int i=;i<=m;i++)
{
cin>>k>>x>>y;
if(k==)
{
add(x,,l);
add(y,,r);
}
else
{
write(sum(y,l)-sum(x-,r));
cout<<endl;
}
}
return ;
}

第三种方法:括号序列法【简称括号法】

假设有一个长度为10的数轴,我们要将区间[ 2 , 5 ]中种树,这时,我们将 2 处放一个左括号 " ( "  ,5处放一个 " )"  ,表示区间 [ 2 , 5 ]种了树。
查询某个区间树的种类,如区间[ 3 , 10],只需统计10之前(包括10)有多少个‘(’,统计3之前有多少个‘)’,(不包括3)。  
如下图所示:

以上就是括号序列的过程。简单的说,就是更新区间[a,b]时,点a记录左括号数,点b记录右括号数,查询区间[a,b]时,即为b之前(包括b)的左括号数-a之前的右括号数。
下面给出非常简练优秀的代码:
 #include <bits/stdc++.h>
using namespace std;
const int N=;
int l[N],r[N];
int n,m;
inline int read()
{
int x=,f=;
char ch=getchar();
while(ch<''||ch>'')
{
if(ch=='-')
f=-;
ch=getchar();
}
while(ch>=''&&ch<='')
{
x=x*+ch-'';
ch=getchar();
}
return x*f;
}
inline void write(int x)
{
if(x<)
{
putchar('-');
x=-x;
}
if(x>)
{
write(x/);
}
putchar(x%+'');
}
int main()
{
int k,x,y;
n=read();
m=read();
for(int i=;i<=m;i++)
{
cin>>k>>x>>y;
if(k==)
{
for(int j=x;j<=n;j+=j&-j)
l[j]++;
for(int j=y;j<=n;j+=j&-j)
r[j]++;
}
else
{
int ans=;
for(int j=y;j;j-=j&-j)
ans+=l[j];
for(int j=x-;j;j-=j&-j)
ans-=r[j];
write(ans);
cout<<endl;
}
}
return ;
}

Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】的更多相关文章

  1. vijos P1448 校门外的树

    描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的--如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:\(K=1\),读入\(l, ...

  2. 【vijos】P1448 校门外的树

    [题意]两种操作,[L,R]种新的树(不覆盖原来的),或查询[L,R]树的种类数.n<=50000. [算法]树状数组||线段树 [题解]这题可以用主席树实现……不过因为不覆盖原来的,所以有更简 ...

  3. Vijos P1103 校门外的树【线段树,模拟】

    校门外的树 描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……, ...

  4. vijos 1448 校门外的树 树状数组

    描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表 ...

  5. vijos 1448 校门外的树 (不是05年普及组那题)

    描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表 ...

  6. Ping pong(树状数组求序列中比某个位置上的数小的数字个数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong Time Limit: 2000/1000 MS (Java/Others) ...

  7. spoj227 树状数组插队序列问题

    插队问题和线段树解决的方式一样,每个结点维护值的信息是该节点之前的空位有多少,然后从后往前插点即可 注意该题要求输出的是从左往右输出每个士兵的等级,即问士兵最后排在第几个位置 /* 树状数组维护前i个 ...

  8. POJ--3321 Apple Tree(树状数组+dfs(序列))

    Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22613 Accepted: 6875 Descripti ...

  9. 【NOI P模拟赛】校门外歪脖树上的鸽子(树链剖分)

    题面 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ l ≤ r ≤ n , 1 ≤ d ≤ 1 0 8 2 ≤ n ≤ 2 × 10^5,1 ≤ m ≤ 2 ...

随机推荐

  1. java获取当前应用的运行信息(内存,线程,运行时间,状态等)

    一:目的 写这一段程序的原因是需要监控部署的的应用是否正常运行,并且显示其运行状态.在进程莫名死掉后甚至可以自动启动该应用. 首先这段代码可以获取的信息如下 /** * 当前进程运行的主机名 */ p ...

  2. react native仿微信性别选择-自定义弹出框

    简述 要实现微信性别选择需要使用两部分的技术: 第一.是自定义弹出框: 第二.单选框控件使用: 效果 实现 一.配置弹出框 弹出框用的是:react-native-popup-dialog(Git地址 ...

  3. 502 VS 504

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/89 首先看一下概念: 502:作为网关或者代理工作的服务器尝试执 ...

  4. 653. Two Sum IV - Input is a BST

    Given a Binary Search Tree and a target number, return true if there exist two elements in the BST s ...

  5. ArcGIS API for JavaScript 4.2学习笔记[19] 搜索小部件——使用更多数据源

    上一篇中提到,空间搜索小部件是Search这个类的实例化,作为视图的ui属性添加进去后,视图就会出现搜索框了. 这节的主体代码和上篇几乎一致,区别就在上篇提及的sources属性. 先看看结果: 由于 ...

  6. 4.sass的分支结构、循环结构、函数

    分支结构 在sass里,可以使用@if让我们根据一些条件来应用特定的样式 结构: @if 条件 { } 如果条件为真的话,括号里的代码就会释放出来 例如: $use-refixes:true; .ro ...

  7. UNIX域协议(命名套接字)

    这里主要介绍命名UNIX域套接字 1.什么是UNIX域套接字Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务通信的一种方式.是进程间通信(IPC)的一种方式.它提供了两类套接字:字 ...

  8. Java中堆与栈

    简单的说:Java把内存划分成两种:一种是栈内存,一种是堆内存. 1:什么是堆内存: 堆内存是是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时 ...

  9. Java_Date_01_判断两个时间相差的天数

    二.参考资料 1.java 判断两个时间相差的天数 2.java计算两个日期之间相差天数和相隔天数详解

  10. python 中一些关键字的区别

    一.raw_input 和input input和raw_input都可以读取控制台的输入,但是input和raw_input在处理数字时是有区别的 1.当输入为纯数字时 input返回的是数值类型, ...