题集+模板:线段树[1]点修改 区间查找 hdu 2795+1394+1754+4302

本文详细介绍了线段树在处理动态范围最小值问题中的应用,包括点修改区间查找的基本模板与实例解析。从hdu4302老鼠吃东西问题入手,深入探讨了如何使用线段树解决此类问题,包括查询最近位置和更新节点等关键操作。通过hdu1754和hdu1394等示例,进一步展示了线段树在实际问题中的灵活运用,如基本查询和逆序数计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线段树 用于处理动态范围最小值问题。

此处给出的   “点修改 区间查找”  是 最基本的内容。

 首先给出模板:(此处给的是储存最大值)

#define ls o<<1 //为方便写程序,使用了 ls 代替 左子树的编号,rs 代替 右子树的编号
#define rs o<<1|1
#define root 1,1,n //用root 表示 编号为1的节点,左边界1,右边界n
int f[MAXN*4];//线段树需要存储的空间为 区间长度的4倍
void build(int o,int l,int r)//构造树
{
    if(l == r) scanf("%d",&f[o]);
    else
    {
        int m = l + (r-l)/2;
        build(ls,l,m);
        build(rs,m+1,r);
        f[o] = max(f[ls],f[rs]);
    }
}
int query(int ql,int qr,int o,int l,int r)//查找
{
    if(ql > r || qr < l) return -inf;
    if(l >= ql && r <= qr) return f[o];
    int ans = -inf,m = l + (r-l)/2;
    ans = max(ans,query(ql,qr,ls,l,m));
    ans = max(ans,query(ql,qr,rs,m+1,r));
    return ans;
}
void update(int q,int v,int o,int l,int r)//更新节点
{
    if(l==r) {f[o] = v;return;}
    int m = l+(r-l)/2;
    if(q <= m) update(q,v,ls,l,m);
    else update(q,v,rs,m+1,r);
    f[o] = max(f[ls],f[rs]);
}

hdu 4302(点击进入):老鼠吃东西(我也不知道是不是老鼠,反正是个动物),有很多做法,此处使用线段树。

当前所在位置now,他要找最近的位置,那么就是搜索now的右边区间最左边的点,和now的左边区间最右边的点。然后比较搜到的两个点。

由于有一个是查找最左,一个查找最右,所以写了两个查找函数。看起里代码长,实际上都是复制了一遍。当然,只写一个查找函数也是可以实现的。

另外,我当时写的时候把所有点右移一格,实际上没有必要,懒得改。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 100001;
const int inf = 0x3f3f3f3f;
const int M = 11;
const int eps = -(1<<29);

int now,f[4*MAXN],dir;//dir表示老鼠面对的方向,此题f数组储存的是区间内食物的数量
void update(int p,int v,int o,int l,int r)
{
    int m = l + (r-l)/2;
    f[o] += v;
    if(l==r)return;
    if(p <= m) update(p,v,2*o,l,m);
    else update(p,v,2*o+1,m+1,r);
}
int query(int ql,int qr,int o,int l,int r)
{
    if(ql > r || qr < l) return -inf;//判断无效区间
    if(!f[o]) return -inf;//判断有无食物
    if(l==r) return l;
    int m = l+(r-l)/2,ans;
    ans = query(ql,qr,o*2+1,m+1,r);
    if(ans != -inf)return ans;
    ans = query(ql,qr,o*2,l,m);
    return ans;
}
int query2(int ql,int qr,int o,int l,int r)
{
    if(ql > r || qr < l) return inf;//判断无效区间
    if(!f[o]) return inf;//判断有无食物
    if(l==r) return l;
    int m = l+(r-l)/2,ans;
    ans = query2(ql,qr,o*2,l,m);
    if(ans != inf)return ans;
    ans = query2(ql,qr,o*2+1,m+1,r);
    return ans;
}
int main()
{
   // fre();
    int t,L,n,a,b,lto,rto,ans,kase = 0;;
    scanf("%d",&t);
    while(t--)
    {
        dir = 1; now = 1;
        ans = 0;
        MS(f,0);//init;
        scanf("%d%d",&L,&n);//input
        ++L;//所有坐标左移一格

        while(n--)//solve
        {
            scanf("%d",&a);
            if(a==0)
            {
                scanf("%d",&b);
                update(b+1,1,1,1,L);//由于左移一格,因此b+1
            }
            if(a==1)
            {
                lto = query(1,now,1,1,L);
                rto = query2(now,L,1,1,L);
                if(lto == -inf && rto == inf) continue;
                if(abs(lto-now) < abs(rto-now) || (abs(lto-now) == abs(rto-now) && dir == 0))
                {
                    ans += abs(lto-now);
                    now = lto;
                    dir = 0;
                }
                else if(abs(lto-now) > abs(rto-now) || (abs(lto-now) == abs(rto-now) && dir == 1))
                {
                    ans += abs(rto-now);
                    now = rto;
                    dir = 1;
                }
                update(now,-1,1,1,L);
            }
        }
        printf("Case %d: %d\n",++kase,ans);
    }
}


hdu 1754(点击打开) :最基本的题目

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define root 1,1,n
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 200001;
const int inf = 0x3f3f3f3f;

int f[MAXN*4];
void build(int o,int l,int r)
{
    if(l == r) scanf("%d",&f[o]);
    else
    {
        int m = l + (r-l)/2;
        build(ls,l,m);
        build(rs,m+1,r);
        f[o] = max(f[ls],f[rs]);
    }
}
int query(int ql,int qr,int o,int l,int r)
{
    if(ql > r || qr < l) return -inf;
    if(l >= ql && r <= qr) return f[o];
    int ans = -inf,m = l + (r-l)/2;
    ans = max(ans,query(ql,qr,ls,l,m));
    ans = max(ans,query(ql,qr,rs,m+1,r));
    return ans;
}
void update(int q,int v,int o,int l,int r)
{
    if(l==r) {f[o] = v;return;}
    int m = l+(r-l)/2;
    if(q <= m) update(q,v,ls,l,m);
    else update(q,v,rs,m+1,r);
    f[o] = max(f[ls],f[rs]);
}
int main()
{
   // fre();
    int n,m,ql,qr;
    char s[5];
    while(~scanf("%d%d",&n,&m))
    {
        build(root);
        while(m--)
        {
            scanf("%s",s);
            scanf("%d%d",&ql,&qr);
            if(s[0]=='Q') printf("%d\n",query(ql,qr,root));
            else update(ql,qr,root);
        }
    }
}

hdu 1394(点击打开): 实际上这里求逆序数用树状数组更快。为了练习仍使用线段树计算原序列的逆序数。

然后可以递推求出其他的逆序数。 每次把最前面一个移到最后面,假设移动的数的值是x,总共n个数,则逆序数 的改变量为n-2*x-1。(容易证明)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define root 1,0,n-1
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 5001;
const int inf = 0x3f3f3f3f;

int f[MAXN*4];

int query(int ql,int qr,int o,int l,int r)
{
    if(ql > r || qr < l) return 0;
    if(l >= ql && r <= qr) return f[o];
    int ans = 0,m = l + (r-l)/2;
    ans += query(ql,qr,ls,l,m);
    ans += query(ql,qr,rs,m+1,r);
    return ans;
}
void update(int q,int o,int l,int r)
{
    f[o]++;
    if(l==r) return;
    int m = l+(r-l)/2;
    if(q <= m) update(q,ls,l,m);
    else update(q,rs,m+1,r);
}
int main()
{
   // fre();
    int n,sum,a[MAXN],ans;
    int i;
    while(~scanf("%d",&n))
    {
        sum = 0;
        MS(f,0);//init
        for(i = 0; i < n; ++i)//input 求逆序数
        {
            scanf("%d",&a[i]);
            sum +=query(a[i]+1,n-1,root);
            update(a[i],root);
        }
        ans = sum;
        for(i = 0; i < n; ++i)//solve 递推求解
        {
            sum += n-2*a[i]-1;
            if(sum < ans) ans = sum;
        }
        printf("%d\n",ans);
    }
}


hdu 2795(点击打开): 数据 h 范围很大,数组存不下。但是发现n最多200000,h超过n是没必要的(想想为什么),所以如果h大于n可以直接另h = n。

f数组储存区间内剩余宽度 w的最大值

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define root 1,1,h
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 200005;
const int inf = 0x3f3f3f3f;

int f[MAXN*4],n,h,w;
void build(int o,int l,int r)//初始状态,全都是剩余w。 其实可以不用这个函数,用循环就可以了。
{
    f[o] = w;
    if(l==r)return;
    int m = l+(r-l)/2;
    build(ls,l,m);
    build(rs,m+1,r);
}
int query(int v,int o,int l,int r)//查找最左边的满足条件的值
{
    if(f[o] < v) return -1;
    if(l==r) return l;
    int m = l+(r-l)/2,ans;
    ans = query(v,ls,l,m);
    if(ans == -1) ans = query(v,rs,m+1,r);
    return ans;
}
void update(int q,int v,int o,int l,int r)
{
    if(l==r) {f[o]-=v;return;}
    int m=l+(r-l)/2,ans = -inf;
    if(q <= m) update(q,v,ls,l,m); else update(q,v,rs,m+1,r);
    f[o] = max(f[ls],f[rs]);
}
int main()
{
    //fre();
    int ans,ww;
    int i;
    while(~scanf("%d%d%d",&h,&w,&n))
    {
        if(h > n) h = n;//优化!
        build(root);//init
        while(n--)//solve
        {
            scanf("%d",&ww);
            ans = query(ww,root);
            if(ans!=-1)update(ans,ww,root);
            printf("%d\n",ans);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值