先膜拜大牛 orz orz orz .... .. .. . . .. . http://www.notonlysuccess.com/index.php/segment-tree-complete/
maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
PushUP(int rt)是把当前结点的信息更新到父结点
PushDown(int rt)是把当前结点的信息更新给儿子结点
rt表示当前子树的根(root),也就是当前所在的结点
整理这些题目后我觉得线段树的题目整体上可以分成以下四个部分:
单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来
hdu1166 敌兵布阵
题意:O(-1)
思路:O(-1)
线段树功能:update:单点增减 query:区间求和
#include<iostream>
#include<cstdio>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=50005;
int sum[maxn<<2];
void PushUP(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l==r){
scanf("%d",&sum[rt]);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R){
return sum[rt];
}
int m=(l+r)>>1;
int ret=0;
if(L<=m)ret+=query(L,R,lson);
if(R>m)ret+=query(L,R,rson);
return ret;
}
void update(int p,int add,int l,int r,int rt){
if(l==r){
sum[rt]+=add;
return;
}
int m=(l+r)>>1;
if(p<=m)update(p,add,lson);
else update(p,add,rson);
PushUP(rt);
}
int main()
{
int T,n,k=1;
scanf("%d",&T);
while(T--)
{
printf("Case %d:\n",k++);
scanf("%d",&n);
build(1,n,1);
char op[10];
while(scanf("%s",op))
{
if(op[0]=='E')break;
int a,b;
scanf("%d%d",&a,&b);
if(op[0]=='Q')printf("%d\n",query(a,b,1,n,1));
else if(op[0]=='S')update(a,-b,1,n,1);
else update(a,b,1,n,1);
}
}
return 0;
}
hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=200005;
int MAX[maxn<<2];
void PushUP(int rt)
{
MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r){
scanf("%d",&MAX[rt]);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R){
return MAX[rt];
}
int m=(l+r)>>1;
int ret=0;
if(L<=m)ret=max(ret,query(L,R,lson));
if(R>m)ret=max(ret,query(L,R,rson));
return ret;
}
void update(int p,int val,int l,int r,int rt){
if(l==r){
MAX[rt]=val;
return;
}
int m=(l+r)>>1;
if(p<=m)update(p,val,lson);
else update(p,val,rson);
PushUP(rt);
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
build(1,n,1);
while(m--)
{
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(op[0]=='Q')printf("%d\n",query(a,b,1,n,1));
else update(a,b,1,n,1);
}
}
return 0;
}
hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 5555;
int sum[maxn<<2];
void PushUP(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt) {
sum[rt] = 0;
if (l == r) return ;
int m = (l + r) >> 1;
build(lson);
build(rson);
}
void update(int p,int l,int r,int rt) {
if (l == r) {
sum[rt] ++;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update(p , lson);
else update(p , rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L , R , lson);
if (R > m) ret += query(L , R , rson);
return ret;
}
struct ss
{
int num,id;
}a[maxn];
bool cmp(ss a,ss b)
{
return a.num<b.num;
}
int x[maxn];
int main() {
int n;
while (~scanf("%d",&n)) {
build(0 , n - 1 , 1);
int sum = 0;
///离散化
for (int i = 0 ; i < n ; i ++) {
scanf("%d",&a[i].num);
a[i].id=i;
}
sort(a,a+n,cmp);
x[a[0].id]=0;
for(int i=1;i<n;i++)
{
if(a[i].num!=a[i-1].num)
x[a[i].id]=i;
else
x[a[i].id]=x[a[i-1].id];
}
///for (int i = 0 ; i < n ; i ++) printf("%d %d\n",i,x[i]);
for (int i = 0 ; i < n ; i ++) {
sum += query(x[i]+1 , n - 1 , 0 , n - 1 , 1);
//The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.
update(x[i] , 0 , n - 1 , 1);
}
int ret = sum;
for (int i = 0 ; i < n ; i ++) {
sum += n - x[i] - x[i] - 1;
ret = min(ret , sum);
}
printf("%d\n",ret);
}
return 0;
}
hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=200005;
int MAX[maxn<<2];
int n,h,w,x,k;
void PushUP(int rt)
{
MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void build(int l,int r,int rt)
{
MAX[rt]=w;//比放在if里面要时间,放if里面的话最后一行要加PushUP(rt);
if(l==r)return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int query(int l,int r,int rt)
{
if(l==r){
MAX[rt]-=x;
return l;
}
int m=(l+r)>>1;
int ret=MAX[rt<<1]>=x?query(lson):query(rson);
//这样只会改变一个节点,真机智啊
PushUP(rt);
return ret;
}
int main()
{
while(~scanf("%d%d%d",&h,&w,&n))
{
k=min(h,n);
build(1,k,1);
for(int i=0;i<n;i++){
scanf("%d",&x);
if(x>MAX[1])printf("-1\n");
else {
printf("%d\n",query(1,k,1));
}
}
}
return 0;
}
POJ2828
题意:有n个人一次到达售票队列,可以插队,问最后队列状态
一开始想着按照插队位置和到达先后顺序sort一下,发现第二组案例都过不了
sort会破坏队伍原型,如下三个案例答案是不同的,sort后就无差别了
4
0 20523
0 31492
1 19243
1 3890
4
0 20523
1 19243
1 3890
0 31492
4
0 20523
1 19243
0 31492
1 3890
此题可以用线段树做,节点表示剩余空位数,由于,题目保证后到的人m前面一定有m-1人(Posi ∈ [0, i − 1] ),所以按输入顺序倒着单点更新即可。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=200005;
int Sum[maxn<<2];
struct ss{
int pos,val,id;
}a[maxn];
int ans[maxn];
int id;
bool cmp(ss a,ss b)
{
return a.id<b.id;
}
void PushUP(int rt)
{
Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
Sum[rt]=r-l+1;//小优化节省50ms
if(l==r){
// Sum[rt]=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
// PushUP(rt);
}
void query(int p,int l,int r,int rt)
{
// printf("%d l=%d r=%d rt=%d %d\n",p,l,r,rt,Sum[rt]);
Sum[rt]--;//小优化节省50ms
if(l==r){
Sum[rt]=0;
id=l;
return;
}
int m=(l+r)>>1;
if(Sum[rt<<1]>=p)query(p,lson);
else query(p-Sum[rt<<1],rson);
// PushUP(rt);
}
int main()
{
int n,i;
while(~scanf("%d",&n)){
build(1,n,1);
for(i=0;i<n;i++){
scanf("%d%d",&a[i].pos,&a[i].val);
}
for(i=n-1;i>=0;i--){
query(a[i].pos+1,1,n,1);
ans[id]=a[i].val;
}
// sort(a,a+n,cmp);//大优化可节省500ms多
for(i=1;i<=n;i++){
if(i==n)printf("%d\n",ans[i]);
else printf("%d ",ans[i]);
}
}
return 0;
}
POJ2886
题意:n个熊孩子按顺时针围成一个圈,每个孩子手中有张卡片,卡片上有数字x (x!=0) ,第一轮从第k个孩子起出列,若x<0,则从他的左边数第x个出列,若x>0,则从他的右边数(-x)个出列,直到只剩一个孩子,每个出列的孩子都会获得f(p)颗糖果,问f(p)最值。f(p)表示p的因子数,p表示出列的次序。
思路:线段数 先算出N个人中,是第几个人(id)跳出来得到的糖果最多。然后模拟id遍 长到第id个人的name
线段树的结点中保存该区间内还剩多少人,每次update 删除一个人。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=500005;
int sum[maxn<<2];
int pos;
struct ss
{
char name[12];
int val;
}a[maxn];
int yin[maxn];
int getyin(int n)
{
int i,j;
yin[1]=0;
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j+=i)yin[j]++;
}
//如果把因子打表,单独算n=500005比每次算慢500ms,不知道为什么
// return max_element(yin+1,yin+n+1)-yin; 时间慢50ms
int ma=yin[1],id=i;
for(i=2;i<=n;i++){
if(yin[i]>ma){
ma=yin[i];
id=i;
}
}
return id;
}
void build(int l,int r,int rt)
{
sum[rt]=r-l+1;
if(l==r){
int x;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
}
void query(int p,int l,int r,int rt){
sum[rt]--;
if(l==r){
pos=l;
return;
}
int m=(l+r)>>1;
if(sum[rt<<1]>=p)query(p,lson);
else query(p-sum[rt<<1],rson);
}
int main()
{
int n,k,i;
while(~scanf("%d%d",&n,&k))
{
build(1,n,1);
for(i=1;i<=n;i++)scanf("%s%d",a[i].name,&a[i].val);
int m=getyin(n)-1;
int mod=n-1;
query(k,1,n,1);
// printf("pos= %d,m= %d\n",pos,yin[m+1]);
//原先是从1-n都算一遍,慢500ms
for(i=0;i<m;i++)
{
//从左起第k个出列
if(a[pos].val>0)k=(k-1+a[pos].val-1)%mod+1;
else k=((k-1+a[pos].val)%mod+mod)%mod+1;
query(k,1,n,1);
// printf("pos=%d %d k=%d\n",pos,mod, k);
mod--;
}
printf("%s %d\n",a[pos].name,yin[m+1]);
}
return 0;
}
成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候
hdu1698 Just a Hook
题意:O(-1)
思路:O(-1)
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=100005;
int sum[maxn<<2],col[maxn<<2];
void PushUP(int rt)
{
// printf("pushup :rt= %d \n",rt);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
if(col[rt]){
// printf("pushdown :rt= %d m=%d\n",rt,m);
col[rt<<1]=col[rt<<1|1]=col[rt];
sum[rt<<1]=(m-(m>>1))*col[rt];
sum[rt<<1|1]=(m>>1)*col[rt];
col[rt]=0;
}
}
void build(int l,int r,int rt)
{
col[rt]=0;//延迟更新标记
if(l==r){
sum[rt]=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R){
return sum[rt];
}
int m=(l+r)>>1;
int ret=0;
if(L<=m)ret+=query(L,R,lson);
if(R>m)ret+=query(L,R,rson);
return ret;
}
void update(int L,int R,int c,int l,int r,int rt){
// printf("----- %d %d %d\n",l,r,rt);
if(l>=L&&r<=R){
col[rt]=c;
sum[rt]=c*(r-l+1);
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m)update(L,R,c,lson);
if(R>m)update(L,R,c,rson);
PushUP(rt);
}
int main()
{
int n,m,T,k=1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
update(a,b,c,1,n,1);
}
printf("Case %d: The total value of the hook is %d.\n",k++,query(1,n,1,n,1));
}
return 0;
}
POJ3468
题意:O(-1)
思路:O(-1)
线段树功能:update:成段增减 query:区间求和
用时1719ms 用数组数组会更快http://kenby.iteye.com/blog/962159
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
const int maxn=100005;
LL sum[maxn<<2],add[maxn<<2];
void PushUP(int rt)
{
// printf("pushup :rt= %d \n",rt);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
if(add[rt]){
// printf("pushdown :rt= %d m=%d\n",rt,m);
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
sum[rt<<1]+=(m-(m>>1))*add[rt];
sum[rt<<1|1]+=(m>>1)*add[rt];
add[rt]=0;
}
}
void build(int l,int r,int rt)
{
add[rt]=0;//延迟更新标记
if(l==r){
scanf("%lld",&sum[rt]);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUP(rt);
}
LL query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R){
return sum[rt];
}
PushDown(rt,r-l+1);//访问子节点1前都要pushdown
int m=(l+r)>>1;
LL ret=0;
if(L<=m)ret+=query(L,R,lson);
if(R>m)ret+=query(L,R,rson);
return ret;
}
void update(int L,int R,int c,int l,int r,int rt){
// printf("----- %d %d %d\n",l,r,rt);
if(l>=L&&r<=R){
add[rt]+=(LL)c;
sum[rt]+=(LL)c*(r-l+1);
return;
}
PushDown(rt,r-l+1);//访问子节点2前都要pushdown
int m=(l+r)>>1;
if(L<=m)update(L,R,c,lson);
if(R>m)update(L,R,c,rson);
PushUP(rt);
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
build(1,n,1);
while(m--)
{
char op[2];
int a,b,c;
scanf("%s",op);
if(op[0]=='C'){
scanf("%d%d%d",&a,&b,&c);
update(a,b,c,1,n,1);
}
else{
scanf("%d%d",&a,&b);
printf("%I64d\n",query(a,b,1,n,1));
}
}
}
return 0;
}
POJ2528
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并非一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖
为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
线段树功能:update:成段替换 query:简单hash
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=10010;
int ll[maxn],rr[maxn];
int X[maxn<<2];
int col[maxn<<4];
bool vis[maxn<<2];
int cnt;
void PushDown(int rt){
if(col[rt]!=-1){
col[rt<<1]=col[rt<<1|1]=col[rt];
col[rt]=-1;//懒惰标记向下转移,上层标记一定要去除,否则更新的还是上层的值
}
}
void build(int l,int r,int rt)
{
col[rt]=-1;
if(l==r){
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int L,int R,int c,int l,int r,int rt){
if(l>=L&&r<=R){
col[rt]=c;
return;
}
PushDown(rt);
int m=(l+r)>>1;
if(L<=m)update(L,R,c,lson);
if(R>m)update(L,R,c,rson);
}
void query(int l,int r,int rt){
if(l==r){
if(col[rt]!=-1&&!vis[col[rt]]){//col[rt]!=-1 有的点没有被更新
vis[col[rt]]=1;
cnt++;
}
return;
}
PushDown(rt);
int m=(l+r)>>1;
query(lson);
query(rson);
}
int Bin(int key,int n,int X[]) {
int l=0,r=n-1,mid;
while (l<r) {
mid=(l+r)>>1;
if(X[mid]>=key)r=mid;
else l=mid+1;
}
return r;
}
int main()
{
int T,i;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
int nn=0;
for(i=0;i<n;i++){
scanf("%d%d",&ll[i],&rr[i]);
X[nn++]=ll[i];
X[nn++]=rr[i];
}
sort(X,X+nn);
int m=1;
for(i=1;i<nn;i++)if(X[i]!=X[i-1])X[m++]=X[i];
for(i=m-1;i>0;i--){
if(X[i]!=X[i-1]+1)X[m++]=X[i-1]+1;
}
sort(X,X+m);
build(1,m,1);//用build与memset时间上没有差别,由于不用求和等操作,用memset更简单
// memset(col,-1,sizeof(col));
for(i=0;i<n;i++){
int l=Bin(ll[i],m,X)+1;//这里用map去查找超时,用二分就不超,不知道为什么
int r=Bin(rr[i],m,X)+1;
update(l,r,i,1,m,1);
}
memset(vis,0,sizeof(vis));
cnt=0;
query(1,m,1);
printf("%d\n",cnt);
}
return 0;
}
/*
2
3
1 10
1 4
5 10
3
1 10
1 4
6 10
*/
poj3225 Help with Intervals
题意:区间操作,交,并,补等
思路:
我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)
U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换
S:[l,r]区间0/1互换
成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记
开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)
线段树功能:update:成段替换,区间异或 query:简单hash
10次WA,总算过了,先贴了,还有的地方不明白
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=65536*2;
int col[maxn<<2],yihuo[maxn<<2];
int vis[maxn];
void Fyihuo(int rt)
{
if(col[rt]!=-1)col[rt]^=1;
else yihuo[rt]^=1;
}
void PushDown(int rt)
{
if(col[rt]!=-1)
{
col[rt<<1]=col[rt<<1|1]=col[rt];
yihuo[rt<<1]=yihuo[rt<<1|1]=yihuo[rt]=0;
col[rt]=-1;
}
if(yihuo[rt]){
Fyihuo(rt<<1);
Fyihuo(rt<<1|1);
yihuo[rt]=0;
}
}
void update(char op,int L,int R,int l,int r,int rt)
{
if(l>=L&&r<=R){
if(op=='U'){
col[rt]=1;
yihuo[rt]=0;
}
else if(op=='D'){
col[rt]=0;
yihuo[rt]=0;
}
else if(op=='C'||op=='S'){
Fyihuo(rt);
}
return ;
}
PushDown(rt);
int m=(l+r)>>1;
if(m>=L)update(op,L,R,lson);
else if(op=='I'||op=='C'){
col[rt<<1]=yihuo[rt<<1]=0;
}
if(m<R)update(op,L,R,rson);
else if(op=='I'||op=='C'){
col[rt<<1|1]=yihuo[rt<<1|1]=0;
}
//学习了,(L,R)以外区间的更新方法
}
void query(int l,int r,int rt)
{
if (col[rt] == 1) {
for (int it = l ; it <= r ; it ++) {
vis[it] = true;
}
return ;
} else if (col[rt] == 0) return ;
/*
if(l==r){
if(col[rt]==1)vis[l]=1;
else vis[l]=0;
//if(l<12)printf("%d %d\n",l,col[rt]);
return;
}
*///不知为什么WA
PushDown(rt);
int m=(l+r)>>1;
query(lson);
query(rson);
}
int main()
{
// freopen("test.in","r",stdin);
char op,x,y;
int a,b;
memset(col,0,sizeof(col));//初始化为-1就WA
memset(yihuo,0,sizeof(yihuo));
while(~scanf("%c %c%d,%d%c\n",&op,&x,&a,&b,&y))
{
// getchar();
a <<= 1 ;
b <<= 1;
if (x == '(') a ++;
if (y == ')') b --;
update(op , a , b , 0 , maxn , 1);
}
query(0 , maxn , 1);
int f=1,flag=0;
for(int i=0;i<maxn;i++)
{
// printf("%d %d\n",i,vis[i]);
if(vis[i]&&f==1){
if(flag)printf(" ");
if(i%2==0)printf("[%d,",i/2);
else printf("(%d,",i/2);
flag=1;
f=0;
}
if(!vis[i]&&f==0){
if((i-1)%2==0)printf("%d]",i/2);
else printf("%d)",i/2);
f=1;
}
}
if (!flag) printf("empty set");
puts("");
return 0;
}
Poj 3667 - Hotel 线段树--区间合并
最近一直在看胡浩的【完全版】线段树,这个题目是在他的blog介绍的
文字没有参考别人的成分
题目大意:Hotel有N(1 ≤ N ≤ 50,000)间rooms,并且所有的rooms都是连续排列在同一边,groups需要check in 房间,要求房间的编号为连续的r..r+Di-1并且r是最小的;visitors同样可能check out,并且他们每次check out都是编号为Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1)的房间,题目的输入有两种样式:
- 1 a : groups需要check in a间编号连续的房间
- 2 a b : visitors check out 房间,其中房间编号是 a…a+b-1
要求对于每次request,输出为groups分配数目为a的房间中编号最小的房间编号
思路:利用线段树建立模型,维护最大连续区间长度,其中区间长度就是对应的房间数目,并且对应区间中最左边的断点就是answer,同时因为需要求出连续区间的最大长度,因此每次PushUp时都将左右区间合并,lsum维护左区间的最大长度,rsum维护右区间的最大长度,sum维护区间1…N中的最大连续区间长度,cover标志对应区间是否为空(没有住客)
具体实现过程:
BuildTree:建立一颗线段树,其中lsum,rsum,sum初始化为对应区间的长度
Query :询问是否有长度为a的连续区间,如果有,返回对应区间的最左边的断点
UpData :更新线段树的信息
PushUp :将左右子区间合并
PushDown :标志向下传,延迟标志,简单来说就是先标志,然后等到下次询问或者更新时再去更新线段树
#include <stdio.h>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int maxn = 50000;
int n, mNum, op, a, b;
int sum[maxn*3], lsum[maxn*3], rsum[maxn*3], cover[maxn*3];
void BuildTree(int l, int r, int rt)
{
cover[rt] = -1;
lsum[rt] = rsum[rt] = sum[rt] = r-l+1;
if (l == r)
return ;
int m = (l+r)>>1;
BuildTree(lson);
BuildTree(rson);
}/* BuildTree */
void PushDown(int rt, int k)
{
if (cover[rt] != -1)
{ /* Lazy Tag */
cover[rt<<1] = cover[rt<<1|1] = cover[rt];
lsum[rt<<1] = rsum[rt<<1] = sum[rt<<1] = cover[rt] ? 0:(k-(k>>1));
lsum[rt<<1|1] = rsum[rt<<1|1] = sum[rt<<1|1] = cover[rt] ? 0:(k>>1);
cover[rt] = -1;
}
}/* PushDown */
int query(int w, int l, int r, int rt)
{
if (l == r)
return 1;
PushDown(rt, r-l+1); /* Push Down */
int m = (l+r)>>1;
if (sum[rt<<1] >= w) /* 左连续区间长度 */
return query(w, lson);
else if (rsum[rt<<1]+lsum[rt<<1|1] >= w) /* 左区间后半部分与右区间左半部分长度 */
return m-rsum[rt<<1]+1;
else /* 右连续区间长度 */
return query(w, rson);
}/* query */
int Max(int x, int y)
{
return (x>y ? x:y);
}/* Max */
void PushUp(int rt, int k)
{
lsum[rt] = lsum[rt<<1]; /* 左区间的左半部分 */
rsum[rt] = rsum[rt<<1|1]; /* 右区间的右半部分 */
if (lsum[rt] == k-(k>>1))
lsum[rt] += lsum[rt<<1|1];
if (rsum[rt] == k>>1)
rsum[rt] += rsum[rt<<1];
sum[rt] = Max(rsum[rt<<1]+lsum[rt<<1|1], Max(sum[rt<<1], sum[rt<<1|1]));
}/* PushUp */
void UpData(int L, int R, int c, int l, int r, int rt)
{
if (L<=l && r<=R)
{
lsum[rt] = rsum[rt] = sum[rt] = c ? 0 : r-l+1;
cover[rt] = c;
return ;
}/* End of If */
PushDown(rt, r-l+1); /* Push Down */
int m = (l+r)>>1;
if (L <= m)
UpData(L, R, c, lson);
if (R > m)
UpData(L, R, c, rson);
PushUp(rt, r-l+1); /* Push Up */
}/* Updata */
int main()
{
scanf("%d %d", &n, &mNum);
BuildTree(1, n, 1); /* BuildTree */
for (int i=1; i<=mNum; ++i)
{
scanf("%d", &op);
if (op == 1)
{ // Request
scanf("%d", &a);
if (sum[1] < a)
printf("0\n"); /* No result */
else
{
int pos = query(a, 1, n, 1);
printf("%d\n", pos);
UpData(pos, pos+a-1, 1, 1, n, 1); /* UpData the interval */
}
}/* End of If */
else
{
scanf("%d %d", &a, &b);
UpData(a, a+b-1, 0, 1, n, 1); /* UpData the interval */
}
}/* End of For */
return 0;
}