树状数组与线段树(二)

树状数组

1.小朋友排队

n 个小朋友站成一排。

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

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

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

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

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

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

输入格式

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

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

输出格式

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

数据范围

1n100000
0Hi1000000

输入样例:

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个整数,表示最终序列。

数据范围

1n,m100000,
1lrn,
1000c1000,
10001000

解题思路:

给定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;

}

 

posted @ 2020-01-28 15:45  清风紫雪  阅读(143)  评论(0编辑  收藏  举报