HDU 6439 Congruence equation(莫比乌斯反演)

Description

给出一个长度为 k k k的序列 A 1 , . . . , A k A_1,...,A_k A1,...,Ak,定义 f ( m ) f(m) f(m)为满足以下条件的 C C C序列的数量:

1.若 A i = − 1 A_i=-1 Ai=1 C i C_i Ci可以取 [ 0 , m ) [0,m) [0,m)中任意整数,否则 C i = A i % m C_i=A_i\%m Ci=Ai%m

2.同余方程 ∑ i = 1 k C i x i ≡ 1 ( m o d   m ) \sum\limits_{i=1}^kC_ix_i\equiv 1(mod\ m) i=1kCixi1(mod m)有解

给出一整数 n n n,求 ∑ m = 1 n f ( m ) \sum\limits_{m=1}^nf(m) m=1nf(m)

Input

第一行一整数 T ​ T​ T表示用例组数,每组用例首先输入两个整数 k , n ​ k,n​ k,n,之后输入一长度为 k ​ k​ k的序列 A i ​ A_i​ Ai

( 1 ≤ T ≤ 250 , 1 ≤ k ≤ 50 , 1 ≤ n ≤ 1 0 9 , − 1 ≤ A i ≤ 1 0 9 ) (1\le T\le 250,1\le k\le 50,1\le n\le 10^9,-1\le A_i\le 10^9) (1T250,1k50,1n109,1Ai109)

Output

输出答案,结果模 1 0 9 + 7 10^9+7 109+7

Sample Input

2
5 10
-1 -1 8 -1 -1
3 20
-1 6 18

Sample Output

Case #1: 24354
Case #2: 140

Solution

方程有解当且仅当 g c d ( C , m ) = 1 gcd(C,m)=1 gcd(C,m)=1,假设 A A A序列中已经确定值的 g c d gcd gcd g g g,尚未确定的数字有 x x x

1.若 g ≠ 0 g\neq 0 g̸=0,记 f ( d ) f(d) f(d)为使得 g c d ( C , m ) = d gcd(C,m)=d gcd(C,m)=d的序列 C C C的个数, F ( d ) F(d) F(d)为使得 d ∣ g c d ( C , m ) d|gcd(C,m) dgcd(C,m)的序列 C C C的个数,则显然有
F ( d ) = ( m d ) x F(d)=(\frac{m}{d})^x F(d)=(dm)x
且由 F ( d ) = ∑ d ∣ i f ( i ) F(d)=\sum\limits_{d|i}f(i) F(d)=dif(i),莫比乌斯反演有
f ( 1 ) = ∑ d ∣ g c d ( g , m ) μ ( d ) F ( d ) = ∑ d ∣ g c d ( g , m ) μ ( d ) ( m d ) x f(1)=\sum\limits_{d|gcd(g,m)}\mu(d)F(d)=\sum\limits_{d|gcd(g,m)}\mu(d)(\frac{m}{d})^x f(1)=dgcd(g,m)μ(d)F(d)=dgcd(g,m)μ(d)(dm)x
进而有
a n s = ∑ m = 1 n ∑ d ∣ g c d ( g , m ) μ ( d ) ( m d ) x = ∑ d ∣ g μ ( d ) ∑ i = 1 ⌊ n d ⌋ i x = ∑ d ∣ g μ ( d ) S ( ⌊ n d ⌋ , x ) ans=\sum\limits_{m=1}^n\sum\limits_{d|gcd(g,m)}\mu(d)(\frac{m}{d})^x=\sum\limits_{d|g}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}i^x=\sum\limits_{d|g}\mu(d)S(\lfloor\frac{n}{d}\rfloor,x) ans=m=1ndgcd(g,m)μ(d)(dm)x=dgμ(d)i=1dnix=dgμ(d)S(dn,x)
其中 S ( n , k ) = ∑ i = 1 i k S(n,k)=\sum\limits_{i=1} i^k S(n,k)=i=1ik,通过伯努利序列即可 O ( k 2 ) O(k^2) O(k2)预处理 O ( k ) O(k) O(k)得到 S ( n , k ) S(n,k) S(n,k),那么该种情况即可 O ( x g ) O(x\sqrt{g}) O(xg )解决

2.若 g = 0 g=0 g=0,将之前的式子稍作修改有
a n s = ∑ m = 1 n ∑ d ∣ m μ ( d ) ( m d ) x = ∑ d = 1 n μ ( d ) S ( ⌊ n d ⌋ , x ) ans=\sum\limits_{m=1}^n\sum\limits_{d|m}\mu(d)(\frac{m}{d})^x=\sum\limits_{d=1}^n\mu(d)S(\lfloor\frac{n}{d}\rfloor,x) ans=m=1ndmμ(d)(dm)x=d=1nμ(d)S(dn,x)
由于 ⌊ n d ⌋ \lfloor\frac{n}{d}\rfloor dn之后 O ( n ) O(\sqrt{n}) O(n )种取值,只要对于每种取值对应的 d d d所处区间 [ l , r ] [l,r] [l,r],知道该区间的 μ \mu μ之和即可,也即求出莫比乌斯函数前缀和即可

A ( n ) = ∑ i = 1 n μ ( i ) A(n)=\sum\limits_{i=1}^n\mu(i) A(n)=i=1nμ(i),由于 ∑ i ∣ d μ ( i ) = [ d = 1 ] \sum\limits_{i|d}\mu(i)=[d=1] idμ(i)=[d=1],故有
1 = ∑ d = 1 n ∑ i ∣ d μ ( i ) = ∑ i = 1 n μ ( i ) ⌊ n i ⌋ = ∑ i = 1 n ∑ j = 1 ⌊ n i ⌋ μ ( j ) = ∑ i = 1 n A ( ⌊ n i ⌋ ) 1=\sum\limits_{d=1}^n\sum\limits_{i|d}\mu(i)=\sum\limits_{i=1}^n\mu(i)\lfloor\frac{n}{i}\rfloor=\sum\limits_{i=1}^n\sum\limits_{j=1}^{\lfloor\frac{n}{i}\rfloor}\mu(j)=\sum\limits_{i=1}^nA(\lfloor\frac{n}{i}\rfloor) 1=d=1nidμ(i)=i=1nμ(i)in=i=1nj=1inμ(j)=i=1nA(in)
进而有
A ( n ) = 1 − ∑ i = 2 n A ( ⌊ n i ⌋ ) A(n)=1-\sum\limits_{i=2}^nA(\lfloor\frac{n}{i}\rfloor) A(n)=1i=2nA(in)
同样分块加速即可,小范围前缀和可以线性筛预处理,较大值每次递归记忆化

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1000005;
#define mod 1000000007
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x;
}
int mul(int x,int y)
{
	ll z=1ll*x*y;
	return z-z/mod*mod;
}
int mu[maxn],p[maxn],res=0,vis[maxn],sum[maxn];
void pre(int n=1e6)
{
	mu[1]=sum[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])p[res++]=i,mu[i]=-1;
		sum[i]=sum[i-1]+mu[i];
		if(sum[i]<0)sum[i]+=mod;
		for(int j=0;j<res&&i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0)
			{
				mu[i*p[j]]=0;
				break;
			}
			mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++)
		if(mu[i]==-1)mu[i]=mod-1;
}
int inv[60],B[60],C[60][60];
void init(int n=55)
{
	C[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	} 
	inv[1]=1;
	for(int i=2;i<=n;i++)inv[i]=mul(mod-mod/i,inv[mod%i]);
	B[0]=1;
	for(int i=1;i<n;i++)
	{
		for(int j=0;j<i;j++)B[i]=add(B[i],mul(C[i+1][j],B[j]));
		B[i]=mod-mul(B[i],inv[i+1]);
	}
}
int S(int n,int k)
{
	if(k==0)return n%mod;
	int ans=0,temp=1;
	for(int i=1;i<=k+1;i++)
	{
		temp=mul(temp,n+1);
		ans=add(ans,mul(mul(C[k+1][i],B[k+1-i]),temp));
	}
	return mul(ans,inv[k+1]);
}
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
int get_mu(int n)
{
	if(n<=1e6)return mu[n];
	int ans=1;
	for(int i=2;i*i<=n;i++)
		if(n%i==0)
		{
			int num=0;
			while(n%i==0)n/=i,num++;
			if(num>1)return 0;
			ans*=-1;
		}
	if(n>1)ans*=-1;
	return add(ans,mod);
}
map<int,int>M;
int A(int n)
{
	if(n<=1e6)return sum[n];
	if(M.find(n)!=M.end())return M[n];
	int ans=1;
	for(int i=2,nxt;i<=n;i=nxt+1)
	{
		nxt=n/(n/i);
		ans=add(ans,mod-mul(nxt-i+1,A(n/i)));
	}
	return M[n]=ans;
}
int Solve(int l,int r,int n,int k)
{
	if(l==r)return mul(get_mu(l),S(n,k));
	return mul(add(A(r),mod-A(l-1)),S(n,k));
}
int Case=1,T,k,n,a[maxn];
int main()
{
	pre();
	init();
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&k,&n);
		int g=0,m=0,ans=0;
		for(int i=1;i<=k;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]!=-1)g=gcd(g,a[i]);
			else m++;
		}
		if(g)
		{
			for(int i=1;i*i<=g;i++)
				if(g%i==0)
				{
					ans=add(ans,Solve(i,i,n/i,m));
					if(i*i!=g)ans=add(ans,Solve(g/i,g/i,n/(g/i),m));
				}
		}
		else
		{
			for(int i=1,nxt;i<=n;i=nxt+1)
			{
				nxt=n/(n/i);
				ans=add(ans,mul(add(A(nxt),mod-A(i-1)),S(n/i,m)));
			}
		}
		printf("Case #%d: %d\n",Case++,ans);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值