【题目】
原题地址
给你一个整数 S S ,让你求所有整数划分的方案数的价值和,价值是个函数。
【题目分析】
不可做,不可做。
这题真的推起来很烦。。。主要是这个 g g 打了个表发现是积性的才搞出来。
【解题思路】
以下推导大部分来自这篇博客。
这个题的三个 type t y p e 直接推看起来就很不可做,因此我们可以考虑每一个 gi g i 的贡献。
首先求一下拆分数,记 sum[i] s u m [ i ] 为 i i 的拆分数,根据五边形数定理以及生成函数可以 O(nn−−√) O ( n n ) 求出来,大概如下。
嗯欧拉函数的倒数就是它的生成函数,我们配合一下五边形数定理得到。
直接求出 n n 的所有拆分方案去统计 F(pi,pj) F ( p i , p j ) 是不现实的,考虑每对 (pi,pj) ( p i , p j ) 出现次数 num[pi][pj] n u m [ p i ] [ p j ] 。
若 pi<pj p i < p j ,假设一个拆分方案中两个数字分别有 a a 个和 b b 个,那么该方案中 (pi,pj) ( p i , p j ) 出现次数就是 a∗b a ∗ b ,换个方式看这个次数就是 x个pi和y个pj(1≤x≤a,1≤y≤b) x 个 p i 和 y 个 p j ( 1 ≤ x ≤ a , 1 ≤ y ≤ b ) 每一种方案在该方案中都出现了一次,故枚举 pi,pj p i , p j 以及两个的数量 numi,numj n u m i , n u m j 得到
F F 的三种形式的取值都是可以暴力预处理的,因此求出了 num n u m 后我们就可以得到 F(pi,pj)=i F ( p i , p j ) = i 的个数 cnt[i](0≤i<k) c n t [ i ] ( 0 ≤ i < k )
进而我们可以得到:
下面的问题转化为如何在时限内得出 g g 数组在 1e7 1 e 7 范围内的取值,如果知道了我们就可以再用 O(k) O ( k ) 的复杂度得到答案。
可以猜测 g g 是一个积性函数(不然做个鬼啊qwq),然后打表发现是对的。
我们可以证明一下这个结论,下面设 (i,j)=gcd(i,j) ( i , j ) = g c d ( i , j )
首先若 (x,y)=1 ( x , y ) = 1
则 (i,xy)=(i,x)∗(i,y),(i,xy)=1⇒gcd(i,m)=gcd(i,n) ( i , x y ) = ( i , x ) ∗ ( i , y ) , ( i , x y ) = 1 ⇒ g c d ( i , m ) = g c d ( i , n )
现在我们只需要求出 g(pk) g ( p k ) ( p p 是素数)的值。
【参考代码】
#include
using namespace std;
typedef long long LL;
const int N=2005,M=1e7+5,mod=1e9+7;
int typ,n,K,pnum,ans;
int pri[664600],tmp[M],cnt[M],g[M],a[M],bo[M];
int sum[N],p[N][N],gcd[N][N],num[N][N];
void up(int &x,int y) {x+=y;if(x>=mod)x-=mod;if(x<0)x+=mod;}
void init()
{
for(int i=1;i<=n;++i)
{
p[i][0]=1;
for(int j=1;j<=n;++j) p[i][j]=(LL)p[i][j-1]*i%K;
}
for(int i=1;i<=n;++i)
gcd[0][i]=gcd[i][0]=gcd[i][i]=i,gcd[i][1]=gcd[1][i]=1;
for(int i=2;i<=n;++i)
for(int j=2;jif(!gcd[i][j]) gcd[i][j]=gcd[j][i-j];
gcd[j][i]=gcd[i][j];
}
sum[0]=1;
for(int i=1;i<=n;++i)
{
for(int j=1,k=1;k<=i;k+=3*j+1,++j)
up(sum[i],(LL)sum[i-k]*(j&1?1:-1));
for(int j=1,k=2;k<=i;k+=3*j+2,++j)
up(sum[i],(LL)sum[i-k]*(j&1?1:-1));
}
g[0]=0;g[1]=1;
for(int i=2;iif(!bo[i]) pri[++pnum]=i,tmp[i]=i,g[i]=2*(i-1);
for(int j=1;j<=pnum && (LL)i*pri[j]*pri[j]]=1;
if(!(i%pri[j]))
{
tmp[i*pri[j]]=tmp[i]*pri[j];
if(tmp[i]^i) g[i*pri[j]]=(LL)g[i/tmp[i]]*g[tmp[i]*pri[j]]%mod;
else g[i*pri[j]]=((LL)pri[j]*g[i]+i*pri[j]-i)%mod;
break;
}
tmp[i*pri[j]]=pri[j];
g[i*pri[j]]=(LL)g[i]*g[pri[j]]%mod;
}
}
}
int f(int x,int y)
{
if(typ==1) return 1%K;
if(typ==2) return gcd[x][y]%K;
return (p[x][y]+p[y][x]+(x^y))%K;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4772.in","r",stdin);
freopen("BZOJ4772.out","w",stdout);
#endif
scanf("%d%d%d",&typ,&n,&K);
for(int i=0;i"%d",&a[i]);
init();
for(int i=1;i<=n;++i)
for(int j=i+1;i+j<=n;++j)
{
int t=f(i,j);
for(int ni=1;ni*i+j<=n;++ni)
for(int nj=1;ni*i+nj*j<=n;++nj)
up(cnt[t],sum[n-ni*i-nj*j]);
}
for(int i=1;i<=n;++i)
{
int t=f(i,i);
for(int ni=1;ni*i<=n;++ni)
{
int s=sum[n-ni*i];
if((ni+1)*i<=n) up(s,-sum[n-(ni+1)*i]);
up(cnt[t],(LL)ni*(ni-1)/2*s%mod);
}
}
for(int i=0;i*g[a[i]]%mod);
printf("%d\n",ans);
return 0;
}
【总结】
当直接推导很不可做时可以考虑每个元素的贡献。
当推导出来的函数需要它是一个积性函数时,它多半就是一个积性函数,我们可以通过打表来验证。