洛谷 P6278 [USACO20OPEN]Haircut G
Description
有长为 的序列
按 依次输出把大于 的 改为 后逆序对的个数。
Constraints
, 。
Solution
平常的逆序对可以直接用树状数组维护,但是这题有多次询问,每次还会对一些数进行“削平”,不能直接处理。
在“削平”中,原数组的一些逆序对会消掉,可以从这里考虑。
设一个逆序对为,当前 的值为 , 思考什么时候这个逆序对会存在或消失:
-
当 时, 再怎么削也仍比 要大,能够存在。
-
当 时, 会被削到比 小或相等,逆序对消失。
倒过来考虑,所以当 从 增加到 时,满足 的逆序对会新产生。
所以在此过程答案每次增加量是序列中 的逆序对个数。
考虑用树状数组维护逆序对,再设 数组累加 数值 的逆序对个数。
因为 数组中的数值可能会有相等,而在不同位置相等的数值对答案都会有贡献,应当累加。
每次询问的答案自然是每次增加量之和,即 的和(不是到 ,原因在上面解释了)。
举个例子助理解:
最后还是得注意树状数组不能处理下标为 的位置,所以要给每个 。
Code
// by youyou2007 in 2022.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#define int long long
#define REP(i, x, y) for(int i = x; i < y; i++)
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define PER(i, x, y) for(int i = x; i > y; i--)
#define per(i, x, y) for(int i = x; i >= y; i--)
#define lc (k << 1)
#define rc (k << 1 | 1)
using namespace std;
const int N = 1E5 + 5;
int n;
int a[N];
int tree[N];
int cnt[N];
int lowbit(int x)
{
return x & (-x);
}
void add(int x, int y)
{
while(x <= n + 1)//因为 a[i] 都 +1 了,最大范围也要 +1
{
tree[x] += y;
x += lowbit(x);
}
}
int query(int x)
{
int res = 0;
while(x > 0)
{
res += tree[x];
x -= lowbit(x);
}
return res;
}
signed main()
{
scanf("%lld", &n);
rep(i, 1, n)
{
scanf("%lld", &a[i]);
a[i]++;//所有 a[i] + 1,避免 0
}
rep(i, 1, n)
{
cnt[a[i]] += query(n + 1) - query(a[i]); //树状数组类似于桶,每次向里面插入元素,query查询逆序对
add(a[i], 1);
}
int ans = 0;
rep(i, 1, n)
{
printf("%lld\n", ans);
ans += cnt[i]; //每次累计 cnt[i] 要在输出后再累计!
}
return 0;
}
标签:
洛谷
, 数据结构——树状数组
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?