线段树专项训练

线段树专项训练

贴一篇个人认为很好的线段树讲解:
https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

  • A - 单点修改区间求和
    hdoj 1166:http://acm.hdu.edu.cn/showproblem.php?pid=1166
    直接套模版,父节点是左右节点的和。

  • B - 单点修改区间最值
    hdoj 1754:http://acm.hdu.edu.cn/showproblem.php?pid=1754
    直接套模版,父节点是左右节点的最大值。

  • C - 求逆序对
    hdoj 1394:http://acm.hdu.edu.cn/showproblem.php?pid=1394
    大意:给出n个数字,范围0~n-1,有n-1种变换,每次把第一个数移动至最后一位,求出在所有移动方法中,存在的逆序对最少是多少组。
    逆序对怎么求?
    考察每一位,从这一位往前看,有多少个比它大的数,记录下来,作为线段树叶子节点值。例如:1 3 6 9 0 8 5 7 4 2,叶子节点分别为 0 0 0 0 4 1 3 2 5 7,构建二叉树,父节点是左右节点的和,在上面这个例子中,tree[1]为22,则该序列逆序对的个数为22。
    得到初始数列逆序对个数后,求后面的逆序对个数有一个公式:sum=sum+(n-1-arr[i])-arr[i]。然后就解决了。


#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=5005;
struct node
{
    int l,r,w;
}tree[4*maxn+1];
int a[5005]={0};

void build(int l,int r,int k)
{
    tree[k].l=l;tree[k].r=r;
    if(l==r)
    {
        tree[k].w=0;
        return ;
    }
    int m=(l+r)/2;
    build(l,m,k*2);
    build(m+1,r,k*2+1);
    tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

void add(int x,int y,int k)
{
    if(tree[k].l==tree[k].r)
    {
        tree[k].w+=y;
        return;
    }
    int m=(tree[k].l+tree[k].r)/2;
    if(x<=m) add(x,y,k*2);
    else add(x,y,k*2+1);
    tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int main()
{
    int n;
    while (cin>>n)
    {
        memset(a,0,sizeof(a));
        int arr[5005]={0};
        for (int i=1;i<=n;i++)
        {
            cin>>arr[i];
        }
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<i;j++)
            {
                if (arr[i]<arr[j])
                {
                    a[i]++;
                }
            }
        }
        build(1,n,1);
        for (int i=1;i<=n;i++)
        {
            add(i,a[i],1);
        }
        int sum=tree[1].w;
        int ans=sum;
        for (int i=1;i<n;i++)
        {
            sum=sum+(n-1-arr[i])-arr[i];
            ans=min(sum,ans);
        }
        cout<<ans<<endl;
    }
}

这是今年四月份写的了,一直在草稿箱里,这个专题后面还有几道题,但是当时没写题解。

现在退役了,发出来吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值