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 ...
随机推荐
- Spring框架中ModelAndView、Model、ModelMap区别
原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别 注意:如果方法 ...
- iOS框架搭建(MVC,自定义TabBar)--微博搭建为例
项目搭建 1.新建一个微博的项目,去掉屏幕旋转 2.设置屏幕方向-->只有竖向 3.使用代码构建UI,不使用storyboard 4.配置图标AppIcon和LaunchImage 将微博资料的 ...
- 链表创建和链表遍历算法的演示_C语言
今天搞了一个多小时,头是疼的,应该是没休息好吧,学习了数据结构这一节,感觉收益良多,下面贴上代码和心得: /*24_链表创建和链表遍历算法的演示*/ # include <stdio.h> ...
- android中Log类的封装
1.为了方便的使用Log打印日志,以及后续方便撤销日志打印,所以对Log类进行封装是一件好事. package market.phone; import android.util.Log; /** * ...
- Visual Representation of SQL Joins
原文:http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins 从视图上介绍了7种不同类型的JOIN ...
- 删除redis 失效节点
cluster forget命令 cluster forget nodeid
- Python学习_06_文件、IO
文件对象 python中的文件操作和c语言比较类似,包括一些缓冲.偏移量的方式. 文件对象可以通过open().file()两个内建方法创建,两个方法并没有什么不同,使用方法和c语言中的fopen() ...
- iOS学习——UI相关小结
1 StoryBoard: 在Info.plist中可以查看Main storyboard,即入口storyboard,默认为main.storyboard,可以修改为自己创建的storybo ...
- UWP 手绘视频创作工具技术分享系列 - Ink & Surface Dial
本篇作为技术分享系列的第四篇,详细讲一下手绘视频中 Surface Pen 和 Surface Dial 的使用场景. 先放一张微软官方商城的图,Surface 的使用中结合了 Surface Pen ...
- AVFoundation 框架初探究(一)
夜深时动笔 前面一篇文章写了视频播放的几种基本的方式,算是给这个系列开了一个头,这里面最想说和探究的就是AVFoundation框架,很想把这个框架不敢说是完全理解,但至少想把它弄明白它里面到底有什么 ...