BZOJ 4818 SDOI2017 序列计数

刚出炉的省选题,还是山东的。

自古山东出数学和网络流,堪称思维的殿堂,比某地数据结构成风好多了。

废话不说上题解。

1.题面

  求:n个数(顺序可更改),值域为[1,m],和为p的倍数,且这些树里面有质数的方案数是多少?

解题报告:

  0% O(n^n)爆搜,没什么好讲的,用来拍DP;

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 20000010;
int n,P[N],tot,m,k;
LL Ans,tim;
bool vis[N];
inline int gi()
{
	int x=0,res=1;char ch=getchar();
	while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)res=-res;ch=getchar();}
	while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-48,ch=getchar();
	return x*res;
}

inline void shai()
{
	vis[1]=1;
	for(int i=2;i<=m;++i)
		{
			if(!vis[i])P[++tot]=i;
			for(int j=1;j<=tot;++j)
				{
					if(i*P[j]>m)break;
					vis[i*P[j]]=true;
					if(i%P[j]==0)break;
				}
		}
}

inline void dfs(int dep,int flag,int sum)
{
	if(dep==n){if(flag==1&&(sum%k==0))Ans++;return;}
	for(int x=1;x<=m;++x)
		if(vis[x]==false)dfs(dep+1,1,sum+x);
		else dfs(dep+1,flag,sum+x);
}
int main()
{
	freopen("in.txt","r",stdin);
	freopen("BL.txt","w",stdout);
	n=gi();m=gi();k=gi();shai();
	dfs(0,0,0);cout<<Ans%20170408<<endl;
}

  

  30% O(nmp)DP;

    注意到每一个数可以任意取,就是很显然的具有DP性质了。那么有两种DP方法:

      1.f[i][j][0/1]表示第i个数,模p为j,有无质数的情况;这种我写到一半停下来了,因为我发现了第二种DP可以优化。

      2.f[i][j]和g[i][j]分别表示瞎几把乱取数和不取质数的情况,求出后相减即可(容斥思想)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 20000010;
const int Mod = 20170408;
int n,P[N],tot,m,p,f[10100][101];
LL Ans,tim;
bool vis[N];

inline int gi()
{
	int x=0,res=1;char ch=getchar();
	while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)res=-res;ch=getchar();}
	while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-48,ch=getchar();
	return x*res;
}

inline void shai()
{
	vis[1]=1;
	for(int i=2;i<=m;++i)
		{
			if(!vis[i])P[++tot]=i;
			for(int j=1;j<=tot;++j)
				{
					if(i*P[j]>m)break;
					vis[i*P[j]]=true;
					if(i%P[j]==0)break;
				}
		}
}

int main()
{
	freopen("in.txt","r",stdin);
	freopen("DP.txt","w",stdout);
	n=gi();m=gi();p=gi();shai();
	f[0][0]=1ll;
	for(int i=0;i<n;++i)
		for(int j=0;j<p;++j)
			for(int k=1;k<=m;++k)
				{
					f[i+1][(j+k)%p]+=f[i][j];
					if(f[i+1][(j+k)%p]>Mod)
						f[i+1][(j+k)%p]-=Mod;
				}
	Ans=(LL)f[n][0];
	memset(f,0,sizeof(f));f[0][0]=1ll;
	for(int i=0;i<n;++i)
		for(int j=0;j<p;++j)
			for(int k=1;k<=m;++k)
				{
					if(!vis[k])continue;
					f[i+1][(j+k)%p]+=f[i][j];
					if(f[i+1][(j+k)%p]>Mod)
						f[i+1][(j+k)%p]-=Mod;
				}
	Ans-=(LL)f[n][0];
	printf("%lld\n",(Ans%Mod+Mod)%Mod);
}

  

   80%:我们发现状态转移方程是一个第一维线性递推,第二维稳定一阶转移。然后发现p只有100,于是就可以上矩阵快速幂。建立转移矩阵就是上面的DP式子。复杂度O(mp+p^3logn)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 20000010;
const int Mod = 20170408;
struct Matrix{
	int T[105][105];
}S0,M0,M1;
int n,P[N],tot,m,p;
LL Ans;
bool vis[N];

inline int gi()
{
	int x=0,res=1;char ch=getchar();
	while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)res=-res;ch=getchar();}
	while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-48,ch=getchar();
	return x*res;
}

inline void shai()
{
	vis[1]=1;
	for(int i=2;i<=m;++i)
		{
			if(!vis[i])P[++tot]=i;
			for(int j=1;j<=tot;++j)
				{
					if(i*P[j]>m)break;
					vis[i*P[j]]=true;
					if(i%P[j]==0)break;
				}
		}
}

inline Matrix Mul(Matrix a,Matrix b,int I,int K,int J)
{
	Matrix S=S0;
	for(int i=0;i<I;++i)
		for(int j=0;j<J;++j)
			for(int k=0;k<K;++k)
				S.T[i][j]=((LL)(S.T[i][j])+((LL)a.T[i][k]*(LL)b.T[k][j]%Mod))%Mod;
	return S;
}

inline Matrix Qpow(Matrix s,Matrix d,int z,int I,int K,int J)
{
	Matrix S=s;
	for(;z;z>>=1,d=Mul(d,d,I,K,J))
		if(z&1)S=Mul(S,d,I,K,J);
	return S;
}

int main()
{
	freopen("in.txt","r",stdin);
	freopen("MT.txt","w",stdout);
	n=gi();m=gi();p=gi();shai();
	M0.T[0][0]=1ll;
	for(int i=0;i<p;++i)
		for(int j=1;j<=m;++j)
			{
				M1.T[i][(i+j)%p]++;
				M1.T[i][(i+j)%p]%=Mod;
			}
	Matrix ans1 = Qpow(M0,M1,n,p,p,p);
	Ans+=ans1.T[0][0];M0=M1=S0;M0.T[0][0]=1ll;
	for(int i=0;i<p;++i)
		for(int j=1;j<=m;++j)
			{
				if(!vis[j])continue;
				M1.T[i][(i+j)%p]++;
				M1.T[i][(i+j)%p]%=Mod;
			}
	Matrix ans2 = Qpow(M0,M1,n,p,p,p);
	Ans-=ans2.T[0][0];
	printf("%lld\n",(Ans%Mod+Mod)%Mod);
}

  

   100%:我们发现时间TLE在于构建矩阵时的mp太大,然后我们发现这个转移重复了很多次,于是可以通过预处理贡献优化到m^2; 

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 20000010;
const int Mod = 20170408;
struct Matrix{
	int T[105][105];
}S0,M0_1,M1_1,M0_2,M1_2,ans;
int n,P[N],tot,m,p,foo[200];
LL Ans;
bool vis[N];

inline int gi()
{
	int x=0,res=1;char ch=getchar();
	while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)res=-res;ch=getchar();}
	while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-48,ch=getchar();
	return x*res;
}

inline void shai()
{
	vis[1]=1;
	for(int i=2;i<=m;++i)
		{
			if(!vis[i])P[++tot]=i;
			for(int j=1;j<=tot;++j)
				{
					if(i*P[j]>m)break;
					vis[i*P[j]]=true;
					if(i%P[j]==0)break;
				}
		}
}

inline Matrix Mul(Matrix a,Matrix b,int I,int K,int J)
{
	Matrix S=S0;
	for(int i=0;i<I;++i)
		for(int j=0;j<J;++j)
			for(int k=0;k<K;++k)
				S.T[i][j]=(S.T[i][j]+(LL)a.T[i][k]*b.T[k][j])%Mod;
	return S;
}

inline Matrix Qpow(Matrix S,Matrix d,int z,int I,int K,int J)
{
	for(;z;z>>=1,d=Mul(d,d,I,K,J))
		if(z&1)S=Mul(S,d,I,K,J);
	return S;
}

int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	n=gi();m=gi();p=gi();shai();
	M0_1.T[0][0]=M0_2.T[0][0]=1ll;
	for(int i=1;i<=m;++i)++foo[i%p];
	for(int i=0;i<p;++i)
		for(int j=0;j<p;++j)
			{
				int st=(i+j)%p;
				M1_1.T[i][st]+=foo[j];
				if(M1_1.T[i][st]>=Mod)
					M1_1.T[i][st]-=Mod;
			}
	ans = Qpow(M0_1,M1_1,n,p,p,p);
	Ans+=ans.T[0][0];
	for(int i=0;i<p;++i)foo[i]=0;
	for(int i=1;i<=m;++i)
		if(vis[i])++foo[i%p];
	for(int i=0;i<p;++i)
		for(int j=0;j<p;++j)
			{
				int st=(i+j)%p;
				M1_2.T[i][st]+=foo[j];
				if(M1_2.T[i][st]>=Mod)
					M1_2.T[i][st]-=Mod;
			}
	ans = Qpow(M0_2,M1_2,n,p,p,p);
	Ans-=ans.T[0][0];
	printf("%lld\n",(Ans%Mod+Mod)%Mod);
}

  

//然后你就华丽的AC了!

 

时间: 2024-10-25 20:38:47

BZOJ 4818 SDOI2017 序列计数的相关文章

[Sdoi2017]序列计数

4818: [Sdoi2017]序列计数 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 317  Solved: 210 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数中,至少有一个数是质数.Alice想知道,有多少个序列满足她的要求. Input 一行三个数,n,m,p. 1<=n<=10^9,1<=m<=2×10^7,1<

[bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛

[Sdoi2017]序列计数 题目大意:https://www.lydsy.com/JudgeOnline/problem.php?id=4818. 题解: 首先列出来一个递推式子 $f[i][0]$表示$i$个任意数的答案. $f[i][1]$表示$i$个合数的答案. 转移的时候发现可以用矩阵优化这个过程. 至于怎么把矩阵建出来,我们可以开个桶来解决这个问题. 代码: #include <bits/stdc++.h> using namespace std; typedef long lon

【BZOJ4818】[Sdoi2017]序列计数 DP+矩阵乘法

[BZOJ4818][Sdoi2017]序列计数 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数中,至少有一个数是质数.Alice想知道,有多少个序列满足她的要求. Input 一行三个数,n,m,p. 1<=n<=10^9,1<=m<=2×10^7,1<=p<=100 Output 一行一个数,满足Alice的要求的序列数量,答案对20170408取模. Sampl

P3702 [SDOI2017]序列计数

P3702 [SDOI2017]序列计数 链接 分析: 代码: #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map

【bzoj4818】 Sdoi2017—序列计数

http://www.lydsy.com/JudgeOnline/problem.php?id=4818 (题目链接) 题意 一个长度为$n$的序列,每个元素是不超过$m$的正整数,且这$n$个数的和是$p$的倍数,这$n$个数中至少有一个是质数,问这样的序列有多少个. Solution md吓死我了,还以为想错了,$p^2\log n$的半天不敢写=.= $f[i][j]$表示忽略质数条件下的长度为$i$,和$mod~p=j$的序列数:$g[i][j]$表示满足没有一个数是质数的情况下长度为$

[bzoj4818][Sdoi2017]序列计数

来自FallDream,未经允许,请勿转载,谢谢.  Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望,这n个数中,至少有一个数是质数.Alice想知道,有多少个序列满足她的要求.答案取模20170408 n<=10^9 m<=2*10^7 p<=100 题解:考虑计算任意选择的答案 然后把质数去掉,再算一次答案,求差即可. 然后这道题数据范围好水啊 p只有100  瞎矩阵乘法都能过 那个要k^3logn 当然,直接生成函

【bzoj4818】[Sdoi2017]序列计数 矩阵乘法

原文地址:http://www.cnblogs.com/GXZlegend/p/6825132.html 题目描述 Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望,这n个数中,至少有一个数是质数.Alice想知道,有多少个序列满足她的要求. 输入 一行三个数,n,m,p. 1<=n<=10^9,1<=m<=2×10^7,1<=p<=100 输出 一行一个数,满足Alice的要求的序列数量,答案对2017

BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法

Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数中,至少有一个数是质数.Alice想知道,有多少个序列满足她的要求. Input 一行三个数,n,m,p. 1<=n<=10^9,1<=m<=2×10^7,1<=p<=100 Output 一行一个数,满足Alice的要求的序列数量,答案对20170408取模. Sample Input 3 5 3 Sample Outp

codevs 5964 [SDOI2017]序列计数

 [题解] 官方题解就两句话. 写了三个版本的不同分值代码.看代码吧. 前导1 //f[i][j][1/0]表示长为i,sum mod p=j,是否已经选了质数的方案数 #include<cstdio> using namespace std; const int mod=20170408; const int N=1e6+1; int tot,prime[N/3];bool check[N]; int n,m,f[2][N][2];int p; void pre(){ n=1e6;check