夏令营501-511NOIP训练18——高二学堂
传送门:QAQQAQ
题意:给你一个数$n$,把它拆分成至多$k$个正整数,使得这些数的和等于$n$且每一个正整数的个数不能超过$4$
拆分的顺序是无序的,但取出每一个数方案是不同的(例如我要拆$1$,就有$4$种方案,因为$4$个“1”是不同的)
思路:依旧神仙题。。满分好像是什么BM算法,但这道题可以用矩阵快速幂卡过去
40分:暴力,我们把$n$种数拆分成$4*n$个数,然后跑01背包就可以了,防止MLE,可以开滚动,但注意转移时要反着来
#include<bits/stdc++.h>
using namespace std;
const int MOD=; int dp[][],n,k,a[]; int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
if(k>n) k=n;
if(n==) break;
memset(dp,,sizeof(dp));
dp[][]=;
for(int i=;i<=*n;i++) a[i]=(i+)/;
for(int i=;i<=*n;i++)
{
for(int j=min(i,k);j>=;j--)
{
for(int t=n;t>=a[i];t--)
{
dp[t][j]=(dp[t][j]+dp[t-a[i]][j-])%MOD;
}
}
}
int ans=;
for(int i=;i<=k;i++) ans=(ans+dp[n][i])%MOD;
printf("%d\n",ans);
}
return ;
}
60分:我们考虑转移时优化一维——即把枚举数的ID这一维优化掉
我们对于转移进行分类讨论:
1.若转移前数列中没有1,那么我们就可以一次性往现数列中加1,并对新加上的1进行“不同化”——即对加进的t个1乘上C(4,t)(这样可以保证2,3,4……都已进行“不同化”)
2.若转移中有1,那么我们就把数列中所有数都加一,使其没有1
我们可以设$dp[i][j][bl]$为和为$i$,取了$j$个数,数列中是否含有1(这种设状态较好理解)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=; ll dp[][][],n,k; void add(ll &x,ll y)
{
x+=y;
if(x>=MOD) x-=MOD;
}
ll c4[]={,,,,}; int main()
{
while(scanf("%lld%lld",&n,&k)!=EOF)
{
if(k>n) k=n;
if(n==&&k==) break;
memset(dp,,sizeof(dp));
dp[][][]=;
for(ll i=;i<=n;i++)
{
for(ll j=;j<=min(i,k);j++)
{
if(i>j)
{
add(dp[i][j][],dp[i-j][j][]);
add(dp[i][j][],dp[i-j][j][]);
}
for(ll t=;t<=min(j,4LL);t++)
add(dp[i][j][],dp[i-t][j-t][]*c4[t]%MOD);
}
}
ll ans=;
for(ll i=;i<=k;i++)
{
add(ans,dp[n][i][]);
add(ans,dp[n][i][]);
}
printf("%lld\n",ans);
}
return ;
}
当然,为了后面的满分代码更方便,我们考虑对状态进行降维,我们把最后bl去掉,把“所有数加1”和“往数组里加1”两个操作一起进行
考虑到满分是矩阵快速幂,我们把剩下的两位压进一维(k比较小,所以可以压)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=; ll dp[],n,k; void add(ll &x,ll y)
{
x+=y;
if(x>=MOD) x-=MOD;
}
ll c4[]={,,,,};
ll id(ll x,ll y)
{
return x*+y;
} int main()
{
while(scanf("%lld%lld",&n,&k)!=EOF)
{
if(k>n) k=n;
if(n==&&k==) break;
memset(dp,,sizeof(dp));
dp[]=;
for(ll i=;i<=n;i++)
{
for(ll j=;j<=min(i,k);j++)
{
for(ll t=;t<=min(j,4LL);t++)
add(dp[id(i,j)],dp[id(i-j,j-t)]*c4[t]%MOD);
//先让原数组j-t个数都加1,再加入t个1
}
}
ll ans=;
for(ll i=;i<=k;i++)
{
add(ans,dp[id(n,i)]);
}
printf("%lld\n",ans);
}
return ;
}
100分:第二天补的。。。其实dp并不需要压入一维,而且dp压维因为转移的时候会涉及到dp[0][0],所以dp压维有点不方便,我们只需要在把dp数组弄进矩阵的时候压一压就可以了
我们考虑转移需要的最早的dp和转移的周期,本来想一个一个递推的,但这种要分类j是否大于4,所以每次矩阵都会改变,无法使用矩阵快速幂。
所以我们加大周期:每十个一次转移,$dp[i][j]$由$dp[i-j][j-t]$转移而来,所以我们对于要更新出的dp[m+1][j],枚举所有合法的t(此时t=j不合法,我们已经枚举了$dp[k][k]$前的所有状态,所以转移前状态$i$一定都大于0,所以此时$j=0$不合法)
所以总结一下,矩阵快速幂由这些要点:转移需要的最早的值,转移周期,和初始矩阵边界条件
我们把前$k*(k-1)$列都设为把ANS矩阵往前推10位,后k列根据$dp[m+1][j]$的转移前缀和系数在适当的位置填上$C(4,t)$,为了方便理解,下面打印一个k=5时的初始转移矩阵
(ANS矩阵时横着的,转移时ANS=ANS*B)
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 $
$1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 $
$0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 $
$0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 $
$0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 $
$0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 $
$0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 $
$0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 $
$0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 $
$0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 $
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=; ll dp[][];
int n,k; void add(ll &x,ll y)
{
x+=y;
if(x>=MOD) x-=MOD;
}
ll c4[]={,,,,};
int id(int x,int y)
{
return (x-)*k+y;
} struct matrix{
ll a[][];
int n,m;
matrix(){}
matrix(int n,int m):n(n),m(m)
{
memset(a,,sizeof(a));
}
void print()
{
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++) printf("%lld ",a[i][j]);
puts("");
}
}
}; matrix operator * (matrix A,matrix B)
{
matrix C(A.n,B.m);
int t=min(A.m,B.n);
for(int i=;i<=C.n;i++)
{
for(int j=;j<=C.m;j++)
{
for(int p=;p<=t;p++)
add(C.a[i][j],A.a[i][p]*B.a[p][j]%MOD);
}
}
return C;
} matrix qpow(matrix B,matrix A,int y)
{
matrix Z=A;
while(y)
{
if(y&) Z=Z*B;
B=B*B;
y>>=;
}
return Z;
} void ready()
{
memset(dp,,sizeof(dp));
dp[][]=;
for(int i=;i<=;i++)
{
for(int j=;j<=min(k,i);j++)
{
for(int t=;t<=min(j,);t++)
add(dp[i][j],dp[i-j][j-t]*c4[t]%MOD);
//先让原数组j-t个数都加1,再加入t个1
}
}
} void make_matrix()
{
matrix A(k*k,k*k);
matrix B(k*k,k*k);
for(int i=;i<=A.m;i++) A.a[i][i]=;
for(int i=;i<=k*k-k;i++) B.a[i+k][i]=;
for(int j=;j<=k;j++)
{
for(int t=;t<=min(,j-);t++) //和已经大于k,不可能再从取数为0的情况下一次转移而来
//dp[2][1]就从dp[1][1]转移而来
{
B.a[id(k+-j,j-t)][k*k-k+j]=c4[t];
}
}
matrix C(,k*k);
for(int i=;i<=k;i++)
{
for(int j=;j<=k;j++)
{
C.a[][id(i,j)]=dp[i][j];
}
}
B=qpow(B,A,n-k);
C=C*B;
ll ans=;
for(int i=;i<=k;i++)
{
add(ans,C.a[][id(k,i)]);
}
printf("%lld\n",ans%MOD);
} int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
if(k>n) k=n;
if(n==&&k==) break;
ready();
if(n<=k)
{
ll ans=;
for(int i=;i<=k;i++) add(ans,dp[n][i]);
printf("%lld\n",ans);
}
else make_matrix();
}
return ;
}
夏令营501-511NOIP训练18——高二学堂的更多相关文章
- XJOI 夏令营501-511NOIP训练18 高二学堂
在美丽的中山纪念中学中,有座高二学堂,同样也是因为一个人,让它们变 成了现在这个样子~那就是我们伟大的级主任.因为他,我们又迎来了一个木有电影,只有对答案的段考日:又迎来了一个不是大礼拜,而是小礼拜的 ...
- test20190802 夏令营NOIP训练18
今天的题很有难度啊.然而我10:40才看题-- 高一学堂 在美丽的中山纪念中学里面,有一座高一学堂.所谓山不在高,有仙则名:水不在深,有龙则灵.高一学堂,因为有了yxr,就成了现在这个样子 = =. ...
- 夏令营501-511NOIP训练18——高三楼
传送门:QAQQAQ 题意:定义矩阵A与矩阵B重复,当且仅当A可以通过任意次行列交换得到B,例如下图A,B即为合法矩阵 现求对于$n*n$的矩阵有多少个不重复的矩阵 数据范围: 对于10%的数据 N≤ ...
- XJOI夏令营501训练1——分配工作
传送门:QAQQAQ 题意:某公司有工作人员x1,x2,…,xn ,他们去做工作y1,y2,…,ym(n<=m) ,每个人都能做其中的几项工作,并且对每一项工作都有一个固定的效率.问能否找到一种 ...
- XJOI 夏令营501-511NOIP训练18 高三楼
参观完各种饭堂,学校还有什么著名的景点呢?当然是教室了,此时此刻我 们来到了高三楼.你会发现高三楼门口会有以身份认证系统,这东西还有着一段疼人的历史.每年的九月到来,高三的童鞋大多不习惯学校的作息时间 ...
- 台州学院maximum cow训练记录
前队名太过晦气,故启用最大牛 我们的组队大概就是18年初,组队阵容是17级生詹志龙.陶源和16级的黄睿博. 三人大学前均无接触过此类竞赛,队伍十分年轻.我可能是我们队最菜的,我只是知道的内容最多,靠我 ...
- Oracle汉字转拼音package
--函数GetHzFullPY(string)用于获取汉字字符串的拼音 --select GetHzFullPY('中华人民共和国') from dual; --返回:ZhongHuaRenMinGo ...
- Linux运维之基础拾遗
第一部分 Linux常用文件管理命令 1.1 cp 文件复制 常用选项 -i # 覆盖之前提醒用户确认 -f # 强制覆盖目标文件 -r # 递归复制目录 -d # 复制符号链接本身而非其指向的源文件 ...
- 获取文本的编码类型(from logparse)
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.F ...
随机推荐
- CSP-S考前各种idea题解乱堆
快要考试了我还是这么菜. 已经没有心思维护我的博客了.开一篇博文吧.可能会记得很乱. 这也许是我OI生涯的最后一篇博文了?? 肯定很长很长. 不可能的.谁知道什么时候我心态恢复就把上面两句话删掉开始在 ...
- 执行 systemctl start firewalld 命令后出现Failed to start firewalld.service: Unit is masked
firewalld服务被锁定,不能添加对应端口 执行命令,即可实现取消服务的锁定 # systemctl unmask firewalld 下次需要锁定该服务时执行 # systemctl mask ...
- go解析markdown转成html
一.代码 package main import ( "fmt" "github.com/microcosm-cc/bluemonday" "gith ...
- nuxt 2.0采坑计之 (引入静态文件css)
nuxt 2.0采坑计之静态文件css 外部引入css 全局引用方法为 (在nuxt.config.js配置中在 module.exports = {} 中添加) head: { meta: ...
- 本地JAR包打入本地mvn仓库
新建目录my-lib,将jar包移动到目录中,添加pom文件(用alipay测试) <project xmlns="http://maven.apache.org/POM/4.0.0& ...
- 2019 牛客多校第六场 D Move
题目链接:https://ac.nowcoder.com/acm/contest/886/D 题解摘自官方题解 题目大意 有 K 个体积相同的箱子,有 N 个体积相同或相异的物品,现要按照如下策略装箱 ...
- UVA 10005 Packing polygons(最小圆覆盖)
裸的模板题 AC代码: #include<cstdio> #include<cmath> #include<algorithm> #include<iostr ...
- hql 语法与详细解释
HQL查询: Criteria查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Lanaguage)查询提供了更加丰富的和灵活的查询特性,因此 Hi ...
- 34-Ubuntu-用户权限-05-超级用户
超级用户 Linux系统中的root(超级用户)账号通常用于系统的维护和管理,对操作系统的所有资源具有访问的权限. 在大多数的Linux版本中,都不推荐直接使用root账号登录系统. 在Linux安装 ...
- hdu6311 /// 欧拉路径 无向图最小路径覆盖 输出正反路径
题目大意: 给定n m 为图的点数和边数 接下来m行 u v 为u到v有一条边 要求最少几笔能画完图的所有边 输出每笔画过的路径编号 正数编号正向 负数编号反向 题解:https://www.cnbl ...