Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】
校门外的树
描述
校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
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
分析:这题目从上午九点写到下午四点,历经七个小时的磨难,只为给大家提供最优质的方法!
这道题我用了三种方法去解决!
第一种:线段树【时间花费最长,也最伤脑的写法】,做法是将[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 ;
}
第三种方法:括号序列法【简称括号法】

以上就是括号序列的过程。简单的说,就是更新区间[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 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】的更多相关文章
- vijos P1448 校门外的树
描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的--如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:\(K=1\),读入\(l, ...
- 【vijos】P1448 校门外的树
[题意]两种操作,[L,R]种新的树(不覆盖原来的),或查询[L,R]树的种类数.n<=50000. [算法]树状数组||线段树 [题解]这题可以用主席树实现……不过因为不覆盖原来的,所以有更简 ...
- Vijos P1103 校门外的树【线段树,模拟】
校门外的树 描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……, ...
- vijos 1448 校门外的树 树状数组
描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表 ...
- vijos 1448 校门外的树 (不是05年普及组那题)
描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表 ...
- Ping pong(树状数组求序列中比某个位置上的数小的数字个数)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong Time Limit: 2000/1000 MS (Java/Others) ...
- spoj227 树状数组插队序列问题
插队问题和线段树解决的方式一样,每个结点维护值的信息是该节点之前的空位有多少,然后从后往前插点即可 注意该题要求输出的是从左往右输出每个士兵的等级,即问士兵最后排在第几个位置 /* 树状数组维护前i个 ...
- POJ--3321 Apple Tree(树状数组+dfs(序列))
Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22613 Accepted: 6875 Descripti ...
- 【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 ...
随机推荐
- SQL Server 数据库引擎怎样记录完整备份后修改过的数据
SQL Server 使用两个内部数据结构跟踪被大容量复制操作修改的区,以及自上次完整备份后修改的区.这些数据结构极大地加快了差异备份的速度.当数据库使用大容量日志恢复模式时,这些数据结构也可以加快将 ...
- Hawk原理:通过IEnumerable实现通用的ETL管道
针对IEnumerable已经有多篇文章,本篇介绍如何使用IEnumerable实现ETL. ETL,是英文 Extract-Transform-Load 的缩写,用来描述将数据从来源端经过萃取(ex ...
- userdel 命令详解
userdel 作用: 删除指定用户,以及用户相关的文件. 如不加选项,则仅删除用户账号,而不删除相关文件 选项: -f:强制删除用户,即时用户当前已登录 -r:删除用户的同时删除与用户相关的所有文 ...
- for 在项目实战中用的比较多
for循环编程语言中的语句之一,用于循环执行.for循环是开界的,它的一般形式为: for(; <条件表达式>; ) 语句: 初始化通常是一个赋值语句, 它用来给循环控制变量赋初值: 条件 ...
- calling c++ from golang with swig--windows dll (四)
calling c++ from golang with swig--windows dll 四 前面讲述了windows环境下golang如何通过swig调用C++ dll.由于编译c++代码使用了 ...
- SQL奇技淫巧
1.SQL行列转换 问题:假设有张学生成绩表(tb)如下:姓名 课程 分数张三 语文 74张三 数学 83张三 物理 93李四 语文 74李四 数学 84李四 物理 94想变成(得到如下结果): 姓名 ...
- Python文件读写 - 读一个文件所有行,加工后写另一个文件
#Filename: file_read_and_write.py #打开文件,cNames读取所有行,储存在列表中,循环对每一行在起始处加上序号1,2,3,4 with open(r'file/co ...
- sql sever基本查询语句
查询(*可代表全部)(<>代表不等于于)select 列名 from 表名(,隔开)where 查询条件order by 排序的列名+连接的数据类型必须兼容(结果为字符串数据的连接 , 如 ...
- 一个IC软件工程师的2017年终工作总结
相比2016年的波澜起伏,2017多了一份平静和清淡.不过,平静的生活下,总有一颗飞向远方的心. 在这一年将近结束的时候,总结一下自己的工作,生活和学习.也顺便展望一下未来的2018,看看有哪些 美好 ...
- 关于 const 的一点小研究
在饱受 var 的折磨之后,ES6 终于推出了新的定义变量的方法:let 和 const 和 var 相比,let 和 const 有了自己的作用域,let 用于定义变量,而 const 用于定义常量 ...