[卷积系列] [P3702 [SDOI2017]序列计数]

P3702 [SDOI2017]序列计数

题目:

求 满足下列要求的序列个数:
1.由不超过m的正整数构成的
2.长度为n,而且这n个数的和是p的倍数。
3.n个数中,至少有一个数是质数。
不考虑排列
n < = 1 0 9 n<=10^9 n<=109 , m < = 2 × 1 0 7 m<=2\times 10^7 m<=2×107 , p < = 100 p<=100 p<=100

题解:

这题 O ( n m ) O(n^m) O(nm)的暴搜绝对超时,所以就要从p入手。
然后思考怎么是p的倍数,要最后结果是p的倍数,就表示n个数每个数mod p后的和是0
——摘自洛谷 昕远地自偏
\text{}
感觉思想跟单调队列优化多重背包有点像

因为要求质数数>=1,这个不好控制,转换成总方案数-非质数组成方案数
f ( i ) ( j ) f(i)(j) f(i)(j)表示长度为 i i i 的序列 且和%p为 j j j 的方案数
g ( i ) ( j ) g(i)(j) g(i)(j)表示长度为 i i i 的序列 且序列全部由非质数构成 且和%p为 j j j 的方案数
显然有
枚举每一个数 i i i , f ( 1 ) ( i f(1)(i f(1)(i % p ) + + p)++ p)++
枚举每一个非质数数 i i i , g ( 1 ) ( i g(1)(i g(1)(i % p ) + + p)++ p)++
\text{}
\text{}
当我们合并2个长度分别为 n , m n,m nm 的序列时,显然有 (任意组合,乘法原理)
                                f ( n + m )   ( ( i + j ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f (n+m) \ ((i+j)                                f(n+m) ((i+j) % p ) = f ( n ) ( i ) × f ( m ) ( j ) p) = f(n)( i)\times f(m)(j) p)=f(n)(i)×f(m)(j)
若不看第二维
                                           f ′ ( n + m ) = f ′ ( n ) × f ′ ( m ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f' (n+m) = f'(n)\times f'(m)                                           f(n+m)=f(n)×f(m)
是不是很像 同底数幂的乘法、指数?
                                                                      \text{ \ \ \ \ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \ \ }                                                      a i + j = a i a j a^{i+j}=a^i a^j ai+j=aiaj
或者换个方式理解:
                                                                  \text{ \ \ \ \ \\ \ \ \\ \\ \ \ \\ \\ \ \ \\ \ \ \\ \\ \ \ \\ \\ \ \\ \\ \ \ \\ \\ \ \ \\ \\ \ \ \ \ }                                                   f ′ ( n ) = f ′ ( n / 2 ) 2 f'(n)=f'(n/2)^2 f(n)=f(n/2)2
可以用快速幂来优化,消掉一维
\text{}
\text{}
那么最后的问题就是,维护在快速幂下的单次更新
回到第二维:
f ( i ) f(i) f(i)的含义更新:序列长度为 n , n / 2 , n / 4 , n / 8...... n ,n/2 ,n/4, n/8 .... .. n,n/2,n/4,n/8....... 且和余数为 i 的方案数
  f ( ( i + j ) \ f ((i+j)  f((i+j) % p ) = f ( i ) × f ( j ) p) = f( i)\times f(j) p)=f(i)×f(j)

由于 p < = 100 p<=100 p<=100 ,可以暴力 O ( p 2 ) O(p^2) O(p2) 乘起来
若是 p p p 比较大, 可以考虑 f f t , n t t fft,ntt fft,ntt 优化成 O ( p × l o g 2 p ) O(p\times log_{2}{p}) O(p×log2p)

#include<cstdio>
#define LL long long
const int N=110;
const LL mod=20170408;
int read(){
	int x=0;char c=getchar();
	while(c<48) c=getchar();
	while(c>47) x=x*10+c-'0',c=getchar();
	return x;
}
int pre[2000100],cnt=0,p;
bool v[20001000];
LL f[N],g[N],d[N],F[N],G[N];
void mul(LL *c,LL *a,LL *b)
{
	for(int i=0;i<p;i++) d[i]=0;
	for(int i=0;i<p;i++) 
		for(int j=0;j<p;j++)
			d[(i+j)%p]=(d[(i+j)%p] + a[i]*b[j])%mod;
	for(int i=0;i<p;i++) c[i]=d[i];
}
int main()
{
	int n=read(),m=read();p=read();
	f[1]=g[1]=1;//1%任何数为1,总情况f包括1,非质数g包括1 
	
	for(int i=2;i<=m;i++){
		f[i%p]++;//,printf("f[%d]=%d ",i%p,f[i%p]);//全部构成 
		if(!v[i]) pre[++cnt]=i;
		else g[i%p]++;//,printf("g=%d ",g[i%p]);//非质数构成
		
		for(int j=1;j<=cnt,pre[j]*i<=m;j++){
			v[i*pre[j]]=true; 
			if(i%pre[j]==0) break;
		}
	}//printf("%d ",cnt);
	
	//F[0]=G[0]=1; <-这个是别人的初始化,不会证明,还是用下面这种吧 
	for(int i=0;i<p;i++) F[i]=f[i];
	for(int i=0;i<p;i++) G[i]=g[i]; n--;
	while(n){
		if(n&1==1) mul(F,F,f),mul(G,G,g);
		mul(f,f,f);mul(g,g,g);
		n>>=1;
	}printf("%lld",(F[0]-G[0]+mod)%mod);
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值