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

给出\(a_i\),对于集合\(S\),定义\(f_0(S)=1\)当且仅当存在任意钦定的系数\(c_i\),使得\(\sum_{i\in S}a_ic_i\equiv x\pmod P\),否则\(f_0(S)=0\)

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

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

\(T\le 20,n\le 3*10^5\)

\(1\le x\le 10^{18}\)\(2\le P \le 10^{18}\)\(1\le k\le 10^9\)

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


不可拍的题。。。

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

以下先将\(x\)变成\(x\mod P\)

根据简单的数论知识,可以转化成如下问题:询问是否\(\gcd_{i\in 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_{i\in 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^n\sum_{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_0\subseteq S]\)

一项对\(FWT(f_0(S))\)的贡献为在\(b_0\subseteq 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\n",b[i]);
//		printf("\n");
		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\n",ans);
	}
	return 0;
}
posted @ 2020-11-15 19:15  jz_597  阅读(181)  评论(0编辑  收藏  举报