考点:树状数组,容斥,DP
题意:给定长为 n 的排列,求有多少个排列值互素的逆序对。
知识点:1、树状数组求逆序对;
2、求数组中互质的对数
第一个比较简单,就不讲了。讲一下第二个的模板怎么写,我们可以定义 表示:“所有的gcd 可以是 i 的倍数的数字们组成的数组”的逆序对数,假设我们已经有了这个 dp 数组,那么我们如何将倍数的值转为其值本身,则只需要从大到小枚举 i,并执行:,执行了这一步后, 就转为了:“所有的 gcd 可以等于 i 的数”组成数组的逆序对个数。那么显然 就是我们要求的答案。
代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int n;
int a[N], pos[N];
int tr[N];
LL dp[N];
int lb(int x)
{
return x & -x;
}
void add(int x, int d)
{
for(int i = x; i <= n; i += lb(i)) tr[i] += d;
}
int gsum(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lb(i)) res += tr[i];
return res;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
pos[a[i]] = i;
}
for(int d = n; d >= 1; d -- )
{
for(int kd = n / d * d; kd >= d; kd -= d)
{
dp[d] += (LL)gsum(pos[kd] - 1);
add(pos[kd], 1);
}
for(int kd = d * 2; kd <= n; kd += d)
dp[d] -= dp[kd];
for(int kd = d; kd <= n; kd += d) add(pos[kd], -1);
}
printf("%lld", dp[1]);
return 0;
}