本文涉及知识点
本博文代码打包下载
【C++】树状数组的使用、原理、封装类、样例
[USACO20OPEN] Haircut G
题目描述
Farmer John 由于对整理他难以整平的头发感到疲惫,于是决定去理发。他有一排
N
N
N 缕头发,第
i
i
i 缕头发初始时长度为
A
i
A_i
Ai 微米(
0
≤
A
i
≤
N
0\le A_i\le N
0≤Ai≤N)。理想情况下,他想要他的头发在长度上单调递增,所以他定义他的头发的“不良度”为逆序对的数量:满足
i
<
j
i < j
i<j 及
A
i
>
A
j
A_i > A_j
Ai>Aj 的二元对
(
i
,
j
)
(i,j)
(i,j)。
对于每一个
j
=
0
,
1
,
…
,
N
−
1
j=0,1,\ldots,N-1
j=0,1,…,N−1,Farmer John 想要知道他所有长度大于
j
j
j 的头发的长度均减少到
j
j
j 时他的头发的不良度。
(有趣的事实:人类平均确实有大约 1 0 5 10^5 105 根头发!)
输入格式
输入的第一行包含
N
N
N。
第二行包含
A
1
,
A
2
,
…
,
A
N
A_1,A_2,\ldots,A_N
A1,A2,…,AN。
输出格式
对于每一个 j = 0 , 1 , … , N − 1 j=0,1,\ldots,N-1 j=0,1,…,N−1,用一行输出 Farmer John 头发的不良度。
注意这个问题涉及到的整数大小可能需要使用 64 64 64 位整数型存储(例如,C/C++ 中的“long long”)。
样例 #1
样例输入 #1
5
5 2 3 3 0
样例输出 #1
0
4
4
5
7
提示
样例解释:
输出的第四行描述了 Farmer John 的头发长度减少到
3
3
3 时的逆序对数量。
A
=
[
3
,
2
,
3
,
3
,
0
]
A=[3,2,3,3,0]
A=[3,2,3,3,0] 有五个逆序对:
A
1
>
A
2
,
A
1
>
A
5
,
A
2
>
A
5
,
A
3
>
A
5
,
A_1>A_2,\,A_1>A_5,\,A_2>A_5,\,A_3>A_5,
A1>A2,A1>A5,A2>A5,A3>A5, 和
A
4
>
A
5
A_4>A_5
A4>A5。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 5 1\le N\le 10^5 1≤N≤105。
共 13 13 13 个测试点,其中 1 1 1 为样例,其余性质如下:
测试点
2
2
2 满足
N
≤
100
N\le 100
N≤100。
测试点
3
∼
5
3\sim 5
3∼5 满足
N
≤
5000
N\le 5000
N≤5000。
测试点
6
∼
13
6\sim 13
6∼13 没有额外限制。
出题人:Dhruv Rohatgi
树状数组
f(j)= 将所有长度大于j的头发理成j的逆序对数量。
本题第j行的答案就是f(j)。
f(j)=0
我们计算f(j+1)-f(j)。
i<j,x1=a[i],x2=a[j]。
情况一:x1x2,将j+1理成j前后,都不是逆序对。
情况二:x1 < x2 ,无论如何逆序对不变。
情况三:x1 > x2。如果x1j+1,x2=x。逆序对减少1。其它情况逆序对不变。
f(j+1)-f(j)= 数对(j+1,j)的数量。
由于大于j的头发此时长度都为j+1,故f(j+1)-f(j)=原始x2为j的逆数对数量。
b[j]记录原始数组中x2位i的数量。b的长度N+1,避免溢出。
代码
单元测试
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include <bitset>
using namespace std;
template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
in >> pr.first >> pr.second;
return in;
}
template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) ;
return in;
}
template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
return in;
}
template<class T = int>
vector<T> Read() {
int n;
scanf("%d", &n);
vector<T> ret(n);
for(int i=0;i < n ;i++) {
cin >> ret[i];
}
return ret;
}
template<class T = int>
vector<T> Read(int n) {
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
template<int N = 12 * 1'000'000>
class COutBuff
{
public:
COutBuff() {
m_p = puffer;
}
template<class T>
void write(T x) {
int num[28], sp = 0;
if (x < 0)
*m_p++ = '-', x = -x;
if (!x)
*m_p++ = 48;
while (x)
num[++sp] = x % 10, x /= 10;
while (sp)
*m_p++ = num[sp--] + 48;
}
inline void write(char ch)
{
*m_p++ = ch;
}
inline void ToFile() {
fwrite(puffer, 1, m_p - puffer, stdout);
}
private:
char puffer[N], * m_p;
};
template<int N = 12 * 1'000'000>
class CInBuff
{
public:
inline CInBuff() {
fread(buffer, 1, N, stdin);
}
inline int Read() {
int x(0), f(0);
while (!isdigit(*S))
f |= (*S++ == '-');
while (isdigit(*S))
x = (x << 1) + (x << 3) + (*S++ ^ 48);
return f ? -x : x;
}
private:
char buffer[N], * S = buffer;
};
template<class ELE = int >
class CTreeArr
{
public:
CTreeArr(int iSize) :m_vData(iSize + 1)
{
}
void Add(int index, ELE value)
{
if ((index < 0) || (index >= m_vData.size() - 1)) { return; }
index++;
while (index < m_vData.size())
{
m_vData[index] += value;
index += index & (-index);
}
}
ELE Sum(int index)//[0...index]之和
{
index++;
ELE ret = 0;
while (index)
{
ret += m_vData[index];
index -= index & (-index);
}
return ret;
}
ELE Sum() { return Sum(m_vData.size() - 2); }
ELE Get(int index)
{
return Sum(index) - Sum(index - 1);
}
private:
vector<ELE> m_vData;
};
class Solution {
public:
vector<long long> Ans(vector<int> a) {
const int N = a.size();
CTreeArr<long long> tree(N + 1);
vector<long long> b(N + 1);
for (int i = 0; i < N; i++) {
auto cnt = i - tree.Sum(a[i]);
b[a[i]] += cnt;
tree.Add(a[i], 1);
}
vector<long long> ans = { 0 };
for (int i = 1; i < N; i++) {
ans.emplace_back(ans.back() + b[i-1]);
}
return ans;
}
};
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
int n;
cin >> n;
auto a = Read<int>(n);
auto res = Solution().Ans(a);
for (const auto& i : res)
{
cout << i << endl;
}
#ifdef _DEBUG
/*Out(a, ",a=");
printf("k=%d", k);
*/
#endif // DEBUG
return 0;
}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步优快云学院,听白银讲师(也就是鄙人)的讲解。
https://edu.youkuaiyun.com/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.youkuaiyun.com/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。