树状数组
1. 小朋友排队
n 个小朋友站成一排.
现在要把他们按身高从低到高的顺序排列, 但是每次只能交换位置相邻的两个小朋友.
每个小朋友都有一个不高兴的程度.
开始的时候, 所有小朋友的不高兴程度都是 0.
如果某个小朋友第一次被要求交换, 则他的不高兴程度增加 1, 如果第二次要求他交换, 则他的不高兴程度增加 2(即不高兴程度为 3), 依次类推. 当要求某个小朋友第 k 次交换时, 他的不高兴程度增加 k.
请问, 要让所有小朋友按从低到高排队, 他们的不高兴程度之和最小是多少.
如果有两个小朋友身高一样, 则他们谁站在谁前面是没有关系的.
输入格式
输入的第一行包含一个整数 n, 表示小朋友的个数.
第二行包含 nn 个整数 H1,H2,...,Hn, 分别表示每个小朋友的身高.
输出格式
输出一行, 包含一个整数, 表示小朋友的不高兴程度和的最小值.
数据范围
1≤n≤1000000≤Hi≤1000000
输入样例:
3 3 2 1
输出样例:
9
样例解释
首先交换身高为 3 和 2 的小朋友, 再交换身高为 3 和 1 的小朋友, 再交换身高为 2 和 1 的小朋友, 每个小朋友的不高兴程度都是 3, 总和为 9.
解题思路: 解题关键就是找到最少的交换次数, 我们可能会想到冒泡排序法, 他就是通过不断的交换排序来实现的, 因此我们可以大胆的假设一些规律, 最少的交换次数 == 逆序对的个数.
对于冒泡排序来说, 每次交换最多减少一个逆序数
假设数组有 k 个逆序对,
1至少需要交换 k 次, 因此次数>=k
2在冒泡排序中, 每次交换 (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;
- }
来源: https://www.cnblogs.com/xiaofengzai/p/12238235.html