树状数组与线段树(二)
树状数组
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; }