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太大,然后我们发现这个转移重复了很多次,于是可以通过预处理贡献优化到p^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了!
BZOJ 4818 SDOI2017 序列计数的更多相关文章
- BZOJ.4818.[SDOI2017]序列计数(DP 快速幂)
BZOJ 洛谷 竟然水过了一道SDOI!(虽然就是很水...) 首先暴力DP,\(f[i][j][0/1]\)表示当前是第\(i\)个数,所有数的和模\(P\)为\(j\),有没有出现过质数的方案数. ...
- BZOJ 4818 [Sdoi2017]序列计数 ——矩阵乘法
发现转移矩阵是一个循环矩阵. 然后循环矩阵乘以循环矩阵还是循环矩阵. 据说还有FFT并且更优的做法. 之后再看吧 #include <map> #include <cmath> ...
- bzoj 4818: [Sdoi2017]序列计数【容斥原理+dp+矩阵乘法】
被空间卡的好惨啊---- 参考:http://blog.csdn.net/coldef/article/details/70305596 容斥,\( ans=ans_{没有限制}-ans{没有质数} ...
- 【BZOJ 4818】 4818: [Sdoi2017]序列计数 (矩阵乘法、容斥计数)
4818: [Sdoi2017]序列计数 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 560 Solved: 359 Description Al ...
- [BZOJ4818][SDOI2017]序列计数(动规+快速幂)
4818: [Sdoi2017]序列计数 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 972 Solved: 581[Submit][Status ...
- [BZOJ 4818/LuoguP3702][SDOI2017] 序列计数 (矩阵加速DP)
题面: 传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4818 Solution 看到这道题,我们不妨先考虑一下20分怎么搞 想到暴力,本蒟 ...
- [bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛
[Sdoi2017]序列计数 题目大意:https://www.lydsy.com/JudgeOnline/problem.php?id=4818. 题解: 首先列出来一个递推式子 $f[i][0]$ ...
- [Sdoi2017]序列计数 [矩阵快速幂]
[Sdoi2017]序列计数 题意:长为\(n \le 10^9\)由不超过\(m \le 2 \cdot 10^7\)的正整数构成的和为\(t\le 100\)的倍数且至少有一个质数的序列个数 总- ...
- BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法
BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ...
随机推荐
- 项目实战——企业级Zabbix监控实战(一)
项目实战--企业级Zabbix监控实战 实验一:Zabbix监控的搭建 1.实验准备 centos系统服务器3台. 一台作为监控服务器, 两台台作为被监控节点, 配置好yum源. 防火墙关闭. 各节点 ...
- Unity3D手机斗地主游戏开发实战(04)_出牌判断大小(已完结)
之前我们实现了叫地主.玩家和电脑自动出牌主要功能,但是还有个问题,出牌的时候,没有有效性检查和比较牌力大小.比如说,出牌3,4,5,目前是可以出牌的,然后下家可以出任何牌如3,6,9. 问题1:出牌检 ...
- Model中设置表单验证方法
Model类里面定义$_validate属性支持的验证因子格式: 格式:array(验证字段,验证规则,错误提示,验证条件,附加规则,验证时间). 验证条件: (1)Model::EXISTS_TO_ ...
- Class 与 Style 绑定
将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强.表达式结果的类型除了字符串之外,还可以是对象或数组. 绑定 HTML Class 对象语法 <div cla ...
- [C#]使用Gembox.SpreadSheet向Excel写入数据及图表
本文为原创文章.源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称.作者及网址,谢谢! 开发工具:VS2017 语言:C# DotNet版本:.Net FrameWork 4.0及以 ...
- Bootstrap 开关(switch)使用整理
1.在JS里根据指定的 1或者0 或者其它值让按钮变成 on 或者 off (通常需要根据数据库查询的结果初始化开关的状态) (--开关插件包 bootstrap-switch.min.js) < ...
- ubuntu指令记录
记录一些指令,便于查看使用. 1.sudo -以其他身份运行命令,预设身份为root. 2.ctrl+alt+T打开终端(命令行窗口) 3.如何判断linux系统是32位还是64位,指令:getcon ...
- ibv_open_device()函数
struct ibv_context *ibv_open_device(struct ibv_device *device); 描述 函数会创建一个RDMA设备相关的context:可以通过ibv_c ...
- Netty与传统Server对比
前言 本文旨在介绍传统Socket服务端与NIO服务端的差异. 以餐厅服务员简单举例,每个客人对应一个请求. 传统Socket / OIO public class OioServer { @Supp ...
- lodash源码分析之compact中的遍历
小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头. 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头. 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头. 而现在, 乡愁是一湾浅 ...