ACM新手DAY 17 线段树

题解

A - 敌兵布阵 (样题)

  • 单点更新+区间求和查询
  • 线段树
#include<bits/stdc++.h>
using namespace std;
int sum[50010*4];
void buildtree(int l,int r,int k)//k是当前节点的编号
{
    if(l==r) {cin>>sum[k]; return;}
    int mid=(l+r)/2;//以下是后序遍历部分
    buildtree(l,mid,2*k);
    buildtree(mid+1,r,2*k+1);
    sum[k]=sum[2*k]+sum[2*k+1];
}
///单点更新
void update(int l,int r,int k,int val,int pos)//这里的l,r,k和上面函数里面的含义一样
{
    if(l==r) {sum[k]+=val; return;}
    int mid=(l+r)/2;
    ///下面的if...else部分是找pos节点的
    if(pos<=mid) update(l,mid,2*k,val,pos);
    else update(mid+1,r,2*k+1,val,pos);
    sum[k]=sum[2*k]+sum[2*k+1];
}
///区间查询部分
int query(int l,int r,int ql,int qr,int k)
{
    int mid=(l+r)/2;
    if(l==ql&&r==qr) return sum[k];
    if(ql>mid) return query(mid+1,r,ql,qr,2*k+1);
    else if(qr<=mid) return query(l,mid,ql,qr,2*k);
    else return query(l,mid,ql,mid,2*k)+query(mid+1,r,mid+1,qr,2*k+1);
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int T;
    cin>>T;
    int CS=1;
    string q;
    while(T--)
    {
        int n;
        cin>>n;
        buildtree(1,n,1);
        cout<<"Case "<<CS++<<":"<<endl;
        while(cin>>q)
        {
            if(q[0]=='E') break;
            int a,b;
            cin>>a>>b;
            if(q[0]=='A')
            {
                update(1,n,1,b,a);
            }
            else if(q[0]=='S')
            {
                update(1,n,1,-b,a);
            }
            else if(q[0]=='Q')
            {
                cout<<query(1,n,a,b,1)<<endl;
            }
        }
    }
}

B - I Hate It

  • 区间最大值查询
  • 线段树
  • 线段树的基本应用,把求和处理成最大值。
#include<iostream>
#include<cmath>
using namespace std;
const int mx = 200000;
int Maxn[mx*4];
void buildtree(int l,int r,int k)//k是当前节点的编号
{
    if(l==r) {cin>>Maxn[k]; return;}
    int mid=(l+r)/2;//以下是后序遍历部分
    buildtree(l,mid,2*k);
    buildtree(mid+1,r,2*k+1);
    Maxn[k]=max(Maxn[2*k],Maxn[2*k+1]);
}
///单点更新
void update(int l,int r,int k,int val,int pos)//这里的l,r,k和上面函数里面的含义一样
{
    if(l==r) {Maxn[k] = val; return;}
    int mid=(l+r)/2;
    ///下面的if...else部分是找pos节点的
    if(pos<=mid) update(l,mid,2*k,val,pos);
    else update(mid+1,r,2*k+1,val,pos);
    Maxn[k]=max(Maxn[2*k],Maxn[2*k+1]);
}
///区间查询部分
int query(int l,int r,int ql,int qr,int k)
{
    int mid=(l+r)/2;
    if(l==ql&&r==qr) return Maxn[k];
    if(ql>mid) return query(mid+1,r,ql,qr,2*k+1);
    else if(qr<=mid) return query(l,mid,ql,qr,2*k);
    else return max(query(l,mid,ql,mid,2*k), query(mid+1,r,mid+1,qr,2*k+1));
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int n, m;
    string q;
    while(cin>>n>>m)
    {
        buildtree(1,n,1);
        while(m--)//m次查询
        {
            cin >> q;
            int a,b;
            cin >> a >> b;
            if(q[0]=='U')
            {
                update(1,n,1,b,a);
            }
            else if(q[0]=='Q')
            {
                cout<<query(1,n,a,b,1)<<endl;
            }
        }
    }
    return 0;
}

D - Billboard

题目:有个高h,宽w的公告板,要贴n张公告,每个公告的长度是k,高度固定为1,公告放的要尽可能靠上并尽可能靠左,每给出一张公告,要求这个公告在满足要求的情况下放在了第几层。

按照线段树的做法的话,因为公告的高度固定为1,可以对公告板的高度进行线段花费,将其现在的宽度值存起来,然后每次遍历从左子树开始往下走,知道走到叶子节点满足要求即可。
将原来的模板结合结构体携带信息。

#include <stdio.h>
#include <iostream>
using namespace std;
int h,w,n;
int ans;
struct node
{
    int l,r,n;
} a[1000000];
void buildtree(int l,int r,int i,int w)//建树
{
    a[i].l=l;
    a[i].r=r;
    a[i].n=w;
    if(l!=r)
    {
        int mid=(l+r)/2;
        buildtree(l,mid,2*i,w);
        buildtree(mid+1,r,2*i+1,w);
    }
}
void update(int i,int x)
{
    if(a[i].l == a[i].r)//到了叶子节点,叶子节点的值既是层数
    {
        a[i].n-=x;//该层宽度减少
        ans = a[i].l;
        return ;
    }
    if(x<=a[2*i].n)//符合要求搜左子树
        update(2*i,x);
    else//否则右子树
        update(2*i+1,x);
    a[i].n = max(a[2*i].n,a[2*i+1].n);//将左右子树里能放的最大长度存入父亲节点,进行更新
}
int main()
{
    int k;
    while(~scanf("%d%d%d",&h,&w,&n))
    {
        h = min(h, n);//min!!!!占得高度最大为n,这样就减少了一些情况
        buildtree(1,h,1,w);
        for(int i = 1; i<=n; i++)
        {
            scanf("%d",&k);
            if(a[1].n>=k)//如果这个公告没有超出公告板的长度,那么才能放入
            {
                update(1,k);
                printf("%d\n",ans);
            }
            else
                printf("-1\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值