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

本文探讨了在给定序列条件下,求解特定形式同余方程的解数量问题,利用莫比乌斯反演进行高效计算。通过对序列中已知元素的分析,提出了解决方案,并详细解释了如何处理不同情况下的计算,包括预处理伯努利序列以加速求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出一个长度为kkk的序列A1,...,AkA_1,...,A_kA1,...,Ak,定义f(m)f(m)f(m)为满足以下条件的CCC序列的数量:

1.若Ai=−1A_i=-1Ai=1CiC_iCi可以取[0,m)[0,m)[0,m)中任意整数,否则Ci=Ai%mC_i=A_i\%mCi=Ai%m

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

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

Input

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

(1≤T≤250,1≤k≤50,1≤n≤109,−1≤Ai≤109)(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

输出答案,结果模109+710^9+7109+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

方程有解当且仅当gcd(C,m)=1gcd(C,m)=1gcd(C,m)=1,假设AAA序列中已经确定值的gcdgcdgcdggg,尚未确定的数字有xxx

1.若g≠0g\neq 0g̸=0,记f(d)f(d)f(d)为使得gcd(C,m)=dgcd(C,m)=dgcd(C,m)=d的序列CCC的个数,F(d)F(d)F(d)为使得d∣gcd(C,m)d|gcd(C,m)dgcd(C,m)的序列CCC的个数,则显然有
F(d)=(md)x F(d)=(\frac{m}{d})^x F(d)=(dm)x
且由F(d)=∑d∣if(i)F(d)=\sum\limits_{d|i}f(i)F(d)=dif(i),莫比乌斯反演有
f(1)=∑d∣gcd(g,m)μ(d)F(d)=∑d∣gcd(g,m)μ(d)(md)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
进而有
ans=∑m=1n∑d∣gcd(g,m)μ(d)(md)x=∑d∣gμ(d)∑i=1⌊nd⌋ix=∑d∣gμ(d)S(⌊nd⌋,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=1ikS(n,k)=\sum\limits_{i=1} i^kS(n,k)=i=1ik,通过伯努利序列即可O(k2)O(k^2)O(k2)预处理O(k)O(k)O(k)得到S(n,k)S(n,k)S(n,k),那么该种情况即可O(xg)O(x\sqrt{g})O(xg)解决

2.若g=0g=0g=0,将之前的式子稍作修改有
ans=∑m=1n∑d∣mμ(d)(md)x=∑d=1nμ(d)S(⌊nd⌋,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)
由于⌊nd⌋\lfloor\frac{n}{d}\rfloordn之后O(n)O(\sqrt{n})O(n)种取值,只要对于每种取值对应的ddd所处区间[l,r][l,r][l,r],知道该区间的μ\muμ之和即可,也即求出莫比乌斯函数前缀和即可

A(n)=∑i=1nμ(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=1n∑i∣dμ(i)=∑i=1nμ(i)⌊ni⌋=∑i=1n∑j=1⌊ni⌋μ(j)=∑i=1nA(⌊ni⌋) 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=2nA(⌊ni⌋) 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值