2022 杭电杯(7) I Counting Good Arrays

本题是2022CCPC F题的强化版.

给出一个 n,m 求出所有长度小于等于n的数列\(a_k(k\le n)\)\(a_k\le m\)\(a_i|a_{i+1}\)

固定n 显然可以发现对于m的标准分解 \(m=p_1^{k_1}p_2^{k_2}...\)对于每个p相互独立 所以此时 \(f_w\)\(a_k=w\)的方案数这是一个积性函数。

正常情况下可以通过线性筛来求出答案。不过本题n的长度不定。

固定m \(m=p_1^{k_1}p_2^{k_2}...\) 对于某个n来说方案数是 \(C(n+k1-1,k1)\cdot C(n+k2-1,k2)...\)。推导不难。

本质上是一个关于n的w次多项式.前缀和是一个w+1次的多项式 拉格朗日插值一下仅能求出来对于一个m所有n的和.

继续思考对于某个m 需要知道1~w+1的n的值对于全体m呢 取一个较大的次数c 求出1~c的n各个对所有m的答案和。进行插值即可。

对于某个n来求m m很大线性筛解决不了可以考虑 杜教筛或min25筛.

至于CCPC的F题 只需要一次min25筛即可。不需要插值。

对于这道题 具体的 考虑m中最多的质因子个数 不超过29个 取1~30的n 来算1~m的\(f_i\)的值.

写于通过后:一定要注意到还有常数项 所以尽管是30次多项式需要拿31个点来进行插值,大寄。

对于某个具体的n时 我们可以立刻得到f在\(p^l\)处的值为\(C(l+n-1,n-1)\) 由于n很小我们可以在log的时间内算出.

在p处时取值为\(C(1+n-1,n-1)=n\) 让所有在质数处的取值正确即可这里可设w(x)=1.

那么g(n,j) 表示1~n内要么是质数要么最小质因子大于j的函数点值之和.

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 2000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define a(x) t[x].a
#define sum(x) t[x].sum
#define b(x) t[x].b
#define F first
#define S second
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define add(x,y) (x+y>=mod?x+y-mod:x+y)
using namespace std;
const int MAXN=200010;
//g1i=1 g2i=i;
int maxx,cnt,top,n,m,tr,T;
int g[MAXN],w[MAXN];
int sum[MAXN],id1[MAXN],id2[MAXN],v[MAXN],p[MAXN];
int A[MAXN],B[MAXN],INV[MAXN];
inline void prepare()
{
	rep(2,maxx,i)
	{
		if(!v[i])
		{
			v[i]=p[++top]=i;
			sum[top]=sum[top-1]+1;
		}
		rep(1,top,j)
		{
			if(p[j]>maxx/i)break;
			v[p[j]*i]=p[j];
			if(v[i]==p[j])break;
		}
	}
}
inline void calc()
{
	for(ll i=1,j;i<=m;i=j+1)
	{
		w[++cnt]=m/i;
		j=m/w[cnt];
		g[cnt]=w[cnt]-1;
		if(w[cnt]<=maxx)id1[w[cnt]]=cnt;
		else id2[j]=cnt;
	}
	rep(1,top,i)
	{
		rep(1,cnt,j)
		{
			if(p[i]*p[i]>w[j])break;
			ll cc=w[j]/p[i],k;
			if(cc<=maxx)k=id1[cc];else k=id2[m/cc];
			g[j]=(g[j]-(g[k]-sum[i-1])%mod)%mod;
		}
	}
}
inline int S(int x,int y)//1~x内 最小质因子大于等于y的数的答案和.
{
	if(x<=1||p[y]>x)return 0;
	int k=x<=maxx?id1[x]:id2[m/x];
	int ans=(ll)tr*(g[k]-sum[y-1])%mod;
	for(int i=y,xs=tr;i<=top;++i,xs=tr)
	{
		if(p[i]*p[i]>x)break;
		ll pe=p[i],pp=pe*pe;
		for(int e=1;pp<=x;++e,pe=pp,pp*=p[i])
		{
			ans=(ans+(ll)xs*S(x/pe,i+1))%mod;
			xs=(ll)xs*(tr+e)%mod*INV[e+1]%mod;
			ans=(ans+xs)%mod;
		}
	}
	return ans;
}
inline void lglr(int x)
{
	ll ans=0;
	rep(1,31,i)
	{
		ll cnt=1;
		rep(1,31,j)
		{
			if(i==j)continue;
			cnt=cnt*(x-j)%mod*INV[abs(i-j)]%mod;
			if(i<j)cnt*=-1;
		}
		ans=(ans+cnt*A[i])%mod;
	}
	printf("%lld\n",(ans+mod)%mod);
}
signed main()
{
	freopen("1.in","r",stdin);
	sc(T);INV[1]=1;
	rep(2,100,i)INV[i]=mod-(ll)(mod/i)*INV[mod%i]%mod;
	while(T--)
	{
		sc(n);sc(m);
		if(m==1)
		{
			put(n);
			continue;
		}
		maxx=(int)sqrt(m*1.0)+1;
		prepare();calc();
		int ww=min(n,31);
		rep(1,ww,i)
		{
			//长度为i 对于每个的函数值
			tr=i;
			A[i]=S(m,1)+1;
			A[i]=add(A[i],A[i-1]);
		}
		if(n<=ww)put((A[n]+mod%mod));
		else lglr(n);
		rep(2,maxx,i)v[i]=0;
		top=0;cnt=0;
	}
	return 0;
}
posted @ 2022-11-08 16:14  chdy  阅读(42)  评论(0编辑  收藏  举报