2023.11.14测试

发布时间 2023-11-15 09:28:30作者: xishanmeigao

\[\text{NOIP模拟赛-2023.11.14} \]

T1 简单的题

给三个数 \(n,G,L\),要求从 \(1\dots n\) 中选出一个非空子集使 \(\gcd=G\)\(\operatorname{lcm}=L\)。问方案数。同时有若干询问,给定 \(a_i\),求在包含 \(a_i\) 的前提下的方案数。

\(n,G,L\leq 10^8\)\(Q\leq 5000\)

\(L\) 所有因数,\(G\) 所有倍数取出来,记为 \(a_1,\dots,a_n\)\(n\) 的规模 \(\leq 800\)。把它们写作 \(Gd_1,Gd_2,\dots,Gd_n\) 的形式,现在变成从 \(d\) 里面选数,记选出的为 \(x_1,x_2\dots,x_m\),则 \(\gcd(x_1,\dots ,x_m)=1\)\(\operatorname{lcm}(x_1,\dots,x_m)=\dfrac{L}{G}\)。分解质因数变成每一位因数的指数 \(\max\) 要与 \(L\) 该位相等,指数的 \(\min\)\(0\)。发现 \(\dfrac{L}{G}\) 最多 \(8\) 个质因数,可以状压

\(f_{i,s,t}\) 表示前 \(i\) 位的数的指数 \(\max\) 满足了 \(s\) 集合,指数 \(\min\) 满足了 \(t\) 集合的方案数。枚举 \(i\) 的贡献,则 \(f_{i,s,t}\rightarrow f_{i+1,s',t'}\)。同时 \(i\) 可以不选,\(f_{i,s,t}\rightarrow f_{i+1,s,t}\)。答案即 \(f_{n,S,T}\)

但是现在还要求必须选某个数 \(a_i\)。然后不会了……我的想法是,对于每个询问 \(a_i\),将 \(i-1\)\(i+1\)\(f_{,s,t}\) 记录下来,枚举超集计算贡献(不会高维前缀和),然后合并,时间复杂度 \(\mathcal{O}(800\times 3^{16})\),无法通过。后来想要是一个数 \(x\) 它没有任何一位的指数取到 \(0\),也没有取到 \(\max\),那它在转移的时候转移系数就是 \(0\),所以它的答案应该是 \(\dfrac{f_{n,S,T}}{2}\)。估计不满足这样条件的数应该不会很多,所以其它的暴力做感觉能通过 。

UPD:获得 \(80\) 分,正解就是高维前缀和,时间复杂度 \(\mathcal{O}(8\times 2^{16} \times 800)\)

code
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define LL long long
using namespace std;

bool Mbe;

const int N=800,QQ=5010,SS=(1<<8)+10;
const int MOD=998244853;

int n,G,L,des,Q,S,q[QQ],ans[N];
int a[N],d[N],m;
int p[N][10],cnt,legal[N][2];
int f[N][SS][SS],g[N][SS][SS];
map <int,int> desp;
bool isque[N];

void prework()
{
	for(int i=1; i*i<=L; i++)
	{
		if(L%i==0)
		{
			if(i%G==0 && i<=n)
				a[++m]=i;
			if(i*i!=L && (L/i)%G==0 && L/i<=n)
				a[++m]=L/i;
		}
	}
	sort(a+1,a+1+m);
	for(int i=1; i<=m; i++)
		d[i]=a[i]/G;
}

void divide(int x,int i)
{
	for(int j=2; j*j<=x; j++)
	{
		if(x%j==0)
		{
			if(i==0)
				desp[j]=++cnt;
			int id=desp[j];
			while(x%j==0)
				p[i][id]++,x/=j;
		}
	}
	if(x>1)
	{
		if(i==0)
			desp[x]=++cnt;
		p[i][desp[x]]++;
	}
}

void work(int i)
{
	for(int j=1; j<=cnt; j++)
	{
		if(p[i][j]==p[0][j])
			legal[i][0]^=(1<<j-1);
		if(p[i][j]==0)
			legal[i][1]^=(1<<j-1);
	}
}

void SOS_DP()
{
	for(int i=1; i<=cnt; i++)
		for(int s=0; s<=S; s++)
			if(!(s&(1<<i-1)))
				for(int t=0; t<=S; t++)
					for(int j=1; j<=m; j++)
						(g[j][s][t]+=g[j][s|(1<<i-1)][t])%=MOD;
	for(int i=1; i<=cnt; i++)
		for(int t=0; t<=S; t++)
			if(!(t&(1<<i-1)))
				for(int s=0; s<=S; s++)
					for(int j=1; j<=m; j++)
						(g[j][s][t]+=g[j][s][t|(1<<i-1)])%=MOD;
}

int solve(int i)
{
	int res=0;
	for(int s=0; s<=S; s++)
	{
		for(int t=0; t<=S; t++)
		{
			int ss=s|legal[i][0],tt=t|legal[i][1];
			(res+=1LL*f[i-1][s][t]*g[i+1][S^ss][S^tt]%MOD)%=MOD;
		}
	}
	return res;
}

bool Med;

int main()
{
	freopen("easy.in","r",stdin);
	freopen("easy.out","w",stdout);

	// fprintf(stderr, "%.3lfMB\n",(&Mbe-&Med)/1048576.000);

	scanf("%d%d%d%d",&n,&G,&L,&Q);
	for(int i=1; i<=Q; i++)
		scanf("%d",&q[i]);
	
	if(L%G!=0)
	{
		for(int i=0; i<=Q; i++)
			puts("0");
		return 0;
	}

	prework();

	divide(des=L/G,0);
	for(int i=1; i<=m; i++)
	{
		divide(d[i],i);
		work(i);
	}

	for(int i=1; i<=Q; i++)
	{
		int cur=lower_bound(a+1,a+1+m,q[i])-a;
		if(a[cur]!=q[i])
			continue;
		isque[cur]=1;
	}

	f[0][0][0]=1;  S=(1<<cnt)-1;
	for(int i=0; i<m; i++)
	{
		for(int s=0; s<=S; s++)
		{
			for(int t=0; t<=S; t++)
			{
				(f[i+1][s|legal[i+1][0]][t|legal[i+1][1]]+=f[i][s][t])%=MOD;
				(f[i+1][s][t]+=f[i][s][t])%=MOD;
			}
		}
	}
	g[m+1][0][0]=1;
	for(int i=m+1; i>1; i--)
	{
		for(int s=0; s<=S; s++)
		{
			for(int t=0; t<=S; t++)
			{
				(g[i-1][s|legal[i-1][0]][t|legal[i-1][1]]+=g[i][s][t])%=MOD;
				(g[i-1][s][t]+=g[i][s][t])%=MOD;
			}
		}
	}

	SOS_DP();

	for(int i=1; i<=m; i++)
	{
		if(!isque[i])
			continue;
		ans[i]=solve(i);
	}

	printf("%d\n",f[m][S][S]);
	for(int i=1; i<=Q; i++)
	{
		int cur=lower_bound(a+1,a+1+m,q[i])-a;
		if(a[cur]!=q[i])
			puts("0");
		else
			printf("%d\n",ans[cur]);
	}

	return 0;
}