小苯的逆序对
小苯的逆序对
题目描述
小苯有一个长度为 的排列 。他很想知道这个排列中有多少个逆序对满足互素。
形式化的,有多少个满足 且 且 的 对。
输入描述:
输入包含两行。
第一行一个正整数 。表示排列的长度。
第二行 个正整数 表示排列 ,保证 到 的每个正整数出现且恰好仅出现一次。
输出描述:
输出包含一行一个整数,表示排列 的互素逆序对个数。
示例1
输入
5
5 4 3 2 1
输出
9
示例2
输入
8
1 3 8 7 2 4 6 5
输出
8
示例3
输入
2
1 2
输出
0
备注:
其中 表示 的最大公因数,例如 。
解题思路
D. Counting Rhyme 的变形。现在变成求满足 的逆序对 的数量。
定义 表示 恰好等于 的逆序对的数量,先求出所有 是 的倍数的逆序对的数量,记作 。做法是先把 中所有是 的倍数的元素按原本顺序选出来,然后对选出来的元素用树状数组求逆序对数量。最后根据容斥的思想,有 。倒叙枚举依次求出 即可。
最后答案就是 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int n;
vector<int> g[N];
LL f[N];
int tr[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) {
tr[i] += c;
}
}
int query(int x) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
ret += tr[i];
}
return ret;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
for (int j = 1; j * j <= x; j++) {
if (x % j == 0) {
g[j].push_back(x);
if (x / j != j) g[x / j].push_back(x);
}
}
}
for (int i = n; i; i--) {
LL s = 0;
for (int j = 0; j < g[i].size(); j++) {
s += j - query(g[i][j]);
add(g[i][j], 1);
}
for (int j = i + i; j <= n; j += i) {
s -= f[j];
}
f[i] = s;
for (int j = 0; j < g[i].size(); j++) {
add(g[i][j], -1);
}
}
printf("%lld", f[1]);
return 0;
}
参考资料
题解 | #牛客小白月赛87 #:https://blog.nowcoder.net/n/2804f9f3cb1e4590b1d97bef99b8f28f?
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18017600
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-02-16 台阶-Nim游戏
2022-02-16 二分求解最值问题例题