0%

[CF1986G2] Permutation Problem

神秘的消 题。

题目链接。

题意

给定一个长为 的排列 ,求 且 的 对数。

。

题解

考虑令 ,并使用 。

那么 相当于 。

注意到此时 与 互质,且 与 互质,所以上述条件相当于 且 。

考虑忽略 的条件,先求出所有满足的 ,再去掉 的情况。

维护数组 表示所有 中,满足 且 的 的数量。

那么可以发现,对于一个 来说,合法的 的数量便是 。

(自己思考一下 且 的条件就能明白这为什么是对的。)

考虑这样做的时间复杂度,首先 是一个 的数组,显然开不下,所以需要使用 map 维护。

再来计算处理 数组的复杂度,可以发现最坏需要在 map 中插入 次,所以这部分复杂度是 的。

查询的复杂度完全相同,这样就得到了一个 的做法,可以通过 G1。

核心代码大概长这个样子(其中 存储所有 的因数):

1
2
for(int i=1;i<=n;i++) for(int j:d[y[i]]) vis[x[i]][j]++;
for(int i=1;i<=n;i++) for(int j:d[y[i]]) ans+=vis[j][x[i]];

如果要优化这个做法,必须要去掉一个 ,考虑避免使用 map 来消掉一个 。

想想我们为什么要用 map,原因是 的数组开不下。

但是一个 的数组是开的下的,那么我们枚举一个 ,考虑能否计算所有 的贡献。

维护 是容易的,找到所有 的 ,像上面一样插入即可。

查询也是容易的,发现 只会给所有 的 产生贡献,那么我们直接枚举 就可以了。

特别地,这里我们可以先枚举 再枚举 ,这样这里的枚举总复杂度是 的。

所以我们得到了一个复杂度是 的做法。非常神奇。

代码

上述做法并没有保证 ,所以需要先把 的情况去掉。

容易发现 的情况只会在 时恰好被计算一次(这个条件与 等价),减掉之后就只剩 的情况了,显然 与 的方案数相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
vc<int>num1[500001];
vc<int>num2[500001];
int val[500001];
vc<int>d[500001];
int a[500001];
int n;
int main()
{
for(int i=1;i<=500000;i++) for(int j=1;i*j<=500000;j++) d[i*j].push_back(i);
int T=read();
while(T--)
{
n=read();
for(int i=1;i<=n;i++) a[i]=read();

ll ans=0;
for(int i=1;i<=n;i++)
{
int x=i,y=a[i],g=__gcd(x,y);
x/=g,y/=g;
num1[x].push_back(y);
num2[y].push_back(x);
if(y%x==0) ans--;
}
for(int x=1;x<=n;x++)
{
for(int j:num1[x]) for(int k:d[j]) val[k]++;
for(int y=x;y<=n;y+=x) for(int k:num2[y]) ans+=val[k];
for(int j:num1[x]) for(int k:d[j]) val[k]--;
}
for(int i=1;i<=n;i++) num1[i].clear(),num2[i].clear();
printf("%lld\n",ans/2);
}
return 0;
}

感谢观看!