ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树状数组与线段树(二)

2020-01-28 15:51:41  阅读:223  来源: 互联网

标签:包含 树状 int 线段 交换 整数 数组 小朋友 高兴


树状数组

1.小朋友排队

n 个小朋友站成一排。

现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。

每个小朋友都有一个不高兴的程度。

开始的时候,所有小朋友的不高兴程度都是 0。

如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。

请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。

如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

输入格式

输入的第一行包含一个整数 n,表示小朋友的个数。

第二行包含 nn 个整数 H1,H2,…,Hn,分别表示每个小朋友的身高。

输出格式

输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

数据范围

1≤n≤100000
0≤Hi≤1000000

输入样例:

3
3 2 1

输出样例:

9

样例解释

首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。

 

解题思路:解题关键就是找到最少的交换次数,我们可能会想到冒泡排序法,他就是通过不断的交换排序来实现的,因此我们可以大胆的假设一些规律,最少的交换次数==逆序对的个数。

对于冒泡排序来说,每次交换最多减少一个逆序数

假设数组有k个逆序对,

①至少需要交换k次,因此次数>=k

②在冒泡排序中,每次交换(Ai,Ai+1)且Ai>Ai+1,因此必然会使逆序数减1

综上我们得出最少的交换次数==逆序对的个数

接下来,我们要求每一个小朋友的逆序数,然后根据前n项和公式求出他的不高兴数,在求解单个逆序数时,我们就用到树状数组来统计在该数组中,前方大于他的数和后方小于他的数,总和即为该数的逆序数。

代码:

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1000010;
int tr[N],h[N];
ll con[N];
int n;

int lowbit(int x)
{
    return x&-x;
}

void add(int x,int v)
{
    for(int i=x;i<N;i+=lowbit(i))
        tr[i]+=v;
}
int query(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=tr[i];
    return ans;
}
int main()
{
    int i,j;
    cin>>n;
    for(i=0;i<n;i++)
    {
        cin>>h[i];
        h[i]++;
    }
    //计算前面比他大的数
    long long ans=0;
    for(i=0;i<n;i++)
    {
        con[i]=query(N-1)-query(h[i]);
        add(h[i],1);
    }
    //计算后面比它小的数
    memset(tr,0,sizeof(tr));
    for(i=n-1;i>=0;i--)
    {
        con[i]+=query(h[i]-1);
        add(h[i],1);
    }
    //总和
    for(i=0;i<n;i++)
        ans+=con[i]*(con[i]+1)/2;
    cout<<ans;
    return 0;
}

差分

1.差分

输入一个长度为n的整数序列。

接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。

请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数n和m。

第二行包含n个整数,表示整数序列。

接下来m行,每行包含三个整数l,r,c,表示一个操作。

输出格式

共一行,包含n个整数,表示最终序列。

数据范围

1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000

解题思路:

给定a[1],a[2],...a[n]构造查分数组b[N],使得

a[i]=b[1]+b[2]+...b[i]

核心操作:将a[L~R]全部加上C,等价于b[L]+=C,b[R+1]-=C

1.a[1~L-1]无影响

2.a[L~R]加上了C

3.a[R+1~N]无影响

#include<iostream>
using namespace std;
const int N=100010;
int b[N],q[N];
void insert(int l,int r,int c)
{
    b[l]+=c;
    b[r+1]-=c;
}
int main()
{
    int n,i,j,m,l,r,c;
    cin>>n>>m;
    for(i=1;i<=n;i++)
        cin>>q[i];
    for(i=1;i<=n;i++)
        b[i]=q[i]-q[i-1];
    while(m--)
    {
        cin>>l>>r>>c;
        insert(l,r,c);
    }
    for(i=1;i<=n;i++)
    {
        q[i]=q[i-1]+b[i];
        cout<<q[i]<<" ";
    }
    return 0;

}

 

标签:包含,树状,int,线段,交换,整数,数组,小朋友,高兴
来源: https://www.cnblogs.com/xiaofengzai/p/12238235.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有