牛客挑战赛45 G 致两千年后的你

给出(a_i),对于集合(S),定义(f_0(S)=1)当且仅当存在任意钦定的系数(c_i),使得(sum_{iin S}a_ic_iequiv xpmod P),否则(f_0(S)=0)

定义(f_i(S)=sum_{Tsubseteq S}f_{i-1}(T))

(f_k(U))(U)为全集。

(Tle 20,nle 3*10^5)

(1le xle 10^{18})(2le P le 10^{18})(1le kle 10^9)

保证(P)的最大质因数小于等于(3*10^5)


不可拍的题。。。

考虑第一部分:如何处理这个(f_0)

以下先将(x)变成(xmod P)

根据简单的数论知识,可以转化成如下问题:询问是否(gcd_{iin S}(gcd(a_i,P))|x)。原因大概根据式子(ax+by=gcd(a,b))得出。

为了方便,接下来直接把(gcd(a_i,P))替换成(a_i)

分解质因数,这个东西相当于:对于某个质因数(p_i),存在(a_i),满足这个质因数在(a_i)的指数小于等于在(x)中的指数。

那么每个数可以用(01)(b_i)来表示,其中第(j)位表示(p_j)的指数是否小于等于(x)(p_j)的指数。

于是(f_0(S)=[or_{iin S} b_i=U'])

想到这里再搞另一个问题:如何通过(f_0)得到(f_k)

如果用集合幂级数来表示,发现就是(f_k=f_0*I^k)

思考一下卷(I^k)是什么:先选出一个子集,在这个子集中选出一个子集,再选……选(k-1)遍((I)是只选自己而不是子集,所以是(k-1))。如果将选看成加一,那么最终每个数在([0,k-1])中随机,每个位置有(k)种选择。于是(I^k(S)=k^{|S|})

于是(f_k(U)=sum_{S}f_0(S)k^{n-|S|}),提出(k^n),得到(f_k(U)=k^nsum_{S}f_0(S)k^{-|S|})。这启示着我们将(k^{-1})作为计算(f_0(S))时的系数。

回来思考一下(f_0(S))如何计算。列出式子:(f_0=prod (1+k^{-1}x^{b_i}))。发现(FWT(1+k^{-1}x^{b_i})(S)=1+k^{-1}[b_0subseteq S])

一项对(FWT(f_0(S)))的贡献为在(b_0subseteq S)处给(f_0(S))(1+k^{-1})。于是只需要计算(1+k^{-1})的指数。

定义集合幂级数(g=sum {b_i})(FWT(f_0)(S)=(1+k^{-1})^{FWT(g)(S)}),可以算出(g)再算出(f)

于是这道题就结束了。实现的时候要注意一点细节(拍不出来……),并且卡卡常。

比如说给gcd的速度是很慢的,这时候需要指数取(min)。还有在算(b_i)的时候可以加点剪枝。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300005
#define ll long long
#define mo 1000000007
#define INF 1000000000
ll gcd(ll a,ll b){
	while (b){
		ll k=a%b;
		a=b,b=k;
	}
	return a;
}
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int p[N],np;
bool inp[N];
void initp(int n){
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			if (i%p[j]==0) break;
		}
	}
}
int n;
ll x,k,P;
ll a[N];
int b[N];
int c,d[17],e[17];
int idx(ll &n,int p){
	if (n==0) return INF;
	int t=0;
	while (n%p==0)
		n/=p,++t;
	return t;
}
bool judge(ll &n,int p,int lim){
	int t=0;
	while (t<=lim && n%p==0)
		n/=p,++t;
	return t<=lim;
}
void divide(ll n){
	c=0;
	for (int i=1;i<=np && n!=1;++i)
		if (n%p[i]==0){
			d[c]=p[i];
			e[c]=idx(n,p[i]);
			c++;
		}
	if (n!=1)
		d[c]=n,e[c]=1,c++;
}
void initb(){
	static int bx[N];
	ll xx=x;
	for (int j=0;j<c;++j)
		bx[j]=idx(xx,d[j]);
	for (int i=1;i<=n;++i){
//		ll ai=gcd(a[i],P);
		ll ai=a[i];
		b[i]=0;
//		if (ai==0) continue;
		for (int j=0;j<c;++j){
			if (e[j]<=bx[j]){
				b[i]|=1<<j;
				continue;
			}
			if (judge(ai,d[j],bx[j]))
				b[i]|=1<<j;
		}
	}
}
ll g[1<<17];
int main(){
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	int T;
	scanf("%d%d",&n,&T);
	for (int i=1;i<=n;++i)
		scanf("%lld",&a[i]);
	initp(300000);
	while (T--){
		scanf("%lld%lld%lld",&x,&k,&P);
		x%=P;
		divide(P);
		initb();
//		for (int i=1;i<=n;++i)
//			printf("%d
",b[i]);
//		printf("
");
		memset(g,0,sizeof(ll)*(1<<c));			
		for (int i=1;i<=n;++i)
			g[b[i]]++;
		for (int i=1;i<1<<c;i<<=1)
			for (int j=0;j<1<<c;j+=i<<1)
				for (int k=0;k<i;++k)
					g[j+k+i]+=g[j+k];
		ll tmp=1+qpow(k);
		for (int i=0;i<1<<c;++i)
			g[i]=qpow(tmp,g[i]);
		for (int i=1;i<1<<c;i<<=1)
			for (int j=0;j<1<<c;j+=i<<1)	
				for (int k=0;k<i;++k)
					(g[j+k+i]-=g[j+k])%=mo;
		ll ans=(g[(1<<c)-1]+mo)%mo*qpow(k,n)%mo;
		
		printf("%lld
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/13977888.html