NowCoder 14648 序列(莫比乌斯反演)

NowCoder 14648 序列

Statement

两个长度为 n n n的序列 a , b a,b a,b, 求满足 gcd ⁡ ( x , y ) = 1 \gcd(x,y)=1 gcd(x,y)=1 a b x = b a y a_{b_x}=b_{a_y} abx=bay的的对 ( x , y ) (x,y) (x,y)数量

Solution

A n s = ∑ 1 ≤ x ≤ n 且 1 ≤ y ≤ n [ a b x = b a y ] ⋅ [ gcd ⁡ ( x , y ) = 1 ] Ans=\sum\limits_{1\leq x\leq n且1\leq y\leq n}[a_{b_x}=b_{a_y}]\cdot[\gcd(x,y)=1] Ans=1xn1yn[abx=bay][gcd(x,y)=1].

  • f ( d ) = ∑ 1 ≤ x ≤ n 且 1 ≤ y ≤ n [ a b x = b a y ] [ gcd ⁡ ( x , y ) = d ] f(d)=\sum\limits_{1\leq x\leq n且1\leq y\leq n}[a_{b_x}=b_{a_y}][\gcd(x,y)=d] f(d)=1xn1yn[abx=bay][gcd(x,y)=d].

  • g ( d ) = ∑ d ∣ d ′ f ( d ′ ) = ∑ d ∣ x 且 d ∣ y [ a b x = b a y ] g(d)=\sum_{d|d'}f(d')=\sum\limits_{d|x且d|y}[a_{b_x}=b_{a_y}] g(d)=ddf(d)=dxdy[abx=bay].

    • g ( d ) g(d) g(d)的组合含义是, 我不妨枚举 d ′ d' d满足 d ∣ d ′ d|d' dd那么 d ∣ x d|x dx d ∣ y d|y dy时当且仅当 d = gcd ⁡ ( x , y ) d=\gcd(x,y) d=gcd(x,y)时才满足条件 [ gcd ⁡ ( x , y ) = d ] = t r u e [\gcd(x,y)=d]=true [gcd(x,y)=d]=true. 否则为 f a l s e false false. 那么显然一共 [ a b x = b a y ] [a_{b_x}=b_{a_y}] [abx=bay]只会在 d ′ = gcd ⁡ ( x , y ) d'=\gcd(x,y) d=gcd(x,y)统计一次.
    • 因此我们得到一个等价式 g ( d ) = ∑ d ∣ x 且 d ∣ y [ a b x = b a y ] g(d)=\sum\limits_{d|x且d|y}[a_{b_x}=b_{a_y}] g(d)=dxdy[abx=bay].

我们再考虑莫比乌斯反演的本质: 莫比乌斯反演本质上运用了容斥原理, 当我们用容斥原理蓝理解莫比乌斯反演的时候:

φ ( n ) = ∑ d ∣ n μ ( d ) n d \varphi(n)=\sum_{d|n}\mu(d)\frac{n}{d} φ(n)=dnμ(d)dn为例: 考虑不超过 n n n的正整数 k k k, 根据欧拉函数定义, 我们要计算出有多少个 k k k n n n互质.

考虑质数集合 P = { p ∣ p 是 n 的 质 因 子 } P=\{p|p是n的质因子\} P={ppn}, 当 gcd ⁡ ( n , k ) > 1 \gcd(n,k)>1 gcd(n,k)>1是, 显然 k k k会是 P P P中某些质数的倍数. 这些数都要被去掉. 但很容易发现这样做是很容易重复计数的.

继续考虑两个不同质数 p 1 , p 2 ( p 1 , p 2 ∈ P ) p_1,p_2(p_1,p_2\in P) p1,p2(p1,p2P), 若 p 1 p 2 ∣ k p_1p_2|k p1p2k, 这样的 k k k在之前的操作中扣除了 C 2 1 C_2^1 C21次, 因为 n p 1 p 2 \frac{n}{p_1p_2} p1p2n k k k被多扣除了一次, 所以答案需要加上 n p 1 p 2 \frac{n}{p_1p_2} p1p2n. 继续考虑三个不同质数 p 1 , p 2 , p 3 ( p 1 , p 2 , p 3 ∈ P ) p_1,p_2,p_3(p_1,p_2,p_3\in P) p1,p2,p3(p1,p2,p3P)的情况, 若 p 1 p 2 p 3 ∣ k p_1p_2p_3|k p1p2p3k, 则这样的 k k k在之前操作已经扣除了 C 3 1 − C 3 2 C_3^1-C_3^2 C31C32次, 因此答案需要再次减去 n p 1 p 2 p 3 \frac{n}{p_1p_2p_3} p1p2p3n.

以此类推, 考虑 t t t个不同质数 p 1 , p 2 … , p t ( p 1 , p 2 , … , p t ∈ P ) p_1,p_2\dots,p_t(p_1,p_2,\dots,p_t\in P) p1,p2,pt(p1,p2,,ptP), 若 p 1 p 2 p 3 … p t ∣ k p_1p_2p_3\dots p_t|k p1p2p3ptk, 这样的 k k k在之前操作中被扣除了 C t 1 − C t 2 + C t 3 + ⋯ + ( − 1 ) t C t t − 1 = { 2 t 是 偶 数 0 t 是 奇 数 C_{t}^1-C_t^2+C_t^3+\dots+(-1)^tC_t^{t-1}=\begin{cases}2&t是偶数\\0&t是奇数\end{cases} Ct1Ct2+Ct3++(1)tCtt1={20tt. 然后我们希望 k k k被减去 1 1 1次. 所以呢可以得到 t t t是偶数时应该加上 n p 1 p 2 … p n \frac{n}{p_1p_2\dots p_n} p1p2pnn t t t是奇数时应该减去 n p 1 p 2 … p n \frac{n}{p_1p_2\dots p_n} p1p2pnn. 那么令 d = p 1 p 2 p 3 … p t d=p_1p_2p_3\dots p_t d=p1p2p3pt那么对于欧拉函数而言, 答案就要加上 μ ( d ) n d \mu(d)\frac{n}{d} μ(d)dn.


对于这道题 我们靠考虑 ∑ d = 1 n g ( d ) \sum_{d=1}^ng(d) d=1ng(d)的含义, 显然对于对于每一对数 x , y x,y x,y, 将其 gcd ⁡ ( x , y ) = p 1 s 1 p 2 s 2 … p r s r \gcd(x,y)=p_1^{s_1}p_2^{s_2}\dots p_r^{s_r} gcd(x,y)=p1s1p2s2prsr. 我们考虑对每个质因子进行容斥, 从而达到满足 gcd ⁡ ( x , y ) = 1 \gcd(x,y)=1 gcd(x,y)=1的条件, 类似的我们可以发现如果 p 1 ∣ gcd ⁡ ( x , y ) p_1|\gcd(x,y) p1gcd(x,y)则我们需要将其减去 1 1 1次, 若不同的质因子 p 1 p 2 ∣ gcd ⁡ ( x , y ) p_1p_2|\gcd(x,y) p1p2gcd(x,y)则扣除了 C 2 1 C_2^1 C21次我们需要重新加上和上述容斥过程一致 … \dots 我们就可以得到对于一个数 d = p 1 p 2 … p r d=p_1p_2\dots p_r d=p1p2pr它对于答案的贡献就位 μ ( d ) = ( − 1 ) r \mu(d)=(-1)^r μ(d)=(1)r. 因为上述容斥式子, 我们均考虑只要包含该质因子就好被筛去, 所以显然平方、立方因子也会被筛去, 在 s i = 1 s_i=1 si=1时被恰好筛去了一次. 故不会重复扣除, 就没必要重复容斥了.

所以我们得到了这个式子: A n s = f ( 1 ) = ∑ d = 1 n μ ( d ) g ( d ) Ans=f(1)=\sum\limits_{d=1}^n\mu(d)g(d) Ans=f(1)=d=1nμ(d)g(d). 时间复杂度 O ( ∑ i = 1 n n i ) = O ( n log ⁡ n ) O(\sum_{i=1}^n\frac{n}{i})=O(n\log n) O(i=1nin)=O(nlogn).

Code

# define Fast_IO std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
# include "unordered_map"
# include "algorithm"
# include "iostream"
# include "cstdlib"
# include "cstring"
# include "cstdio"
# include "vector"
# include "bitset"
# include "queue"
# include "cmath"
# include "ctime"
# include "map"
# include "set"
# define ll long long
# define ld long double
# define rep1(i,a,b) for(ll i=(a);i<=(b);i++)
# define rep2(i,a,b) for(ll i=(b);i>=(a);i--)
# define pii pair<int,int>
# define pll pair<ll,ll>
# define ph push_back
# define pb pop_back
# define eb emplace_back
# define vi vector<int>
# define vll vector<ll>
# define vpi vector<pii >
# define vpll vector<pll >
# define ri(x) scanf("%d",&x)
# define rf(x) scanf("%f",&x)
# define rl(x) scanf("%lld",&x)
# define rd(x) scanf("%lf",&x)
# define rs(s) scanf("%s",s+1)
# define wi(x) printf("%d",x)
# define wl(x) printf("%lld",x)
# define ws(s) printf("%s",s+1)
# define resize(v,x) v.resize(x)
# define all(v) v.begin(),v.end()
# define reverse(v) reverse(all(v))
# define fi first
# define se second
# define lowbit(x) ((x)&(-(x)))
# define repauto(Name,v) for(auto Name:v)
# define Endl "\n"
using namespace std;
template<class I> inline I GCD(I A,I B){return B?GCD(B,A%B):A;}
template<class I> inline I LCM(I A,I B){return A/GCD(A,B)*B;}
template<class I> I Sqrt(I N){
	I sqrtN=sqrt(N)-1;
	while(sqrtN+1<=N/(sqrtN+1))sqrtN++;
	return sqrtN;
}
template<class I> I Pow(I X,I Y,__int128 Mod1=998244353){
	static __int128 Ans; Ans=1;
	for(;Y;Y>>=1,X=(__int128)X*X%Mod1) if(Y&1) Ans=Ans*X%Mod1;
	return Ans;
}

namespace Mu_Class{
	int Prime_Cnt;
	int *Prime=new int;
	int *Mu=new int;
	int *Visit=new int;
	int *Num=new int;
	inline void Init(int N){
		static int i,j,Now;
		delete Visit;	Visit=new int[N+1]();
		delete Prime;	Prime=new int[N+1]();
		delete Mu;		Mu=new int[N+1]();
		delete Num;		Num=new int[N+1]();
		for(Mu[1]=1,i=2;i<=N;i++){
			if(!Visit[i]){
				Visit[i]=i,Mu[i]=-1;
				Prime[++Prime_Cnt]=i;
			}for(j=1;j<=Prime_Cnt && Visit[i]>=Prime[j] && Prime[j]<=N/i;j++){
				Mu[i*Prime[j]]=Visit[i]==Prime[j]?0:Mu[i]*(-1);
				Visit[i*Prime[j]]=Prime[j];
			}
		}return;
	}
}using namespace Mu_Class;

const int maxm=1e5+10;

int N;
int A[maxm],B[maxm];
long long Ans;
unordered_map<int,int> Visit1;

long long Solve(int Now){
	static int i;
	static long long Res;
	Visit1.clear();
	for(i=Now;i<=N;i+=Now) Visit1[A[B[i]]]++;
	for(i=Now,Res=0;i<=N;i+=Now) Res+=Visit1[B[A[i]]];
	return Res;
}

int main(){
# ifdef LH_Frank
    freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
# endif
	ri(N);
	rep1(i,1,N) ri(A[i]);
	rep1(i,1,N) ri(B[i]);
	Init(N);
	rep1(i,1,N){
		if(Mu[i]==0) continue;
		Ans+=Mu[i]*Solve(i);
	}wl(Ans);
	return 0;
}

Link

[1] NowCoder 序列

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值