传送门:QAQQAQ

题意:给你一个数$n$,把它拆分成至多$k$个正整数,使得这些数的和等于$n$且每一个正整数的个数不能超过$4$

拆分的顺序是无序的,但取出每一个数方案是不同的(例如我要拆$1$,就有$4$种方案,因为$4$个“1”是不同的)

思路:依旧神仙题。。满分好像是什么BM算法,但这道题可以用矩阵快速幂卡过去

40分:暴力,我们把$n$种数拆分成$4*n$个数,然后跑01背包就可以了,防止MLE,可以开滚动,但注意转移时要反着来

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int MOD=;
  4.  
  5. int dp[][],n,k,a[];
  6.  
  7. int main()
  8. {
  9. while(scanf("%d%d",&n,&k)!=EOF)
  10. {
  11. if(k>n) k=n;
  12. if(n==) break;
  13. memset(dp,,sizeof(dp));
  14. dp[][]=;
  15. for(int i=;i<=*n;i++) a[i]=(i+)/;
  16. for(int i=;i<=*n;i++)
  17. {
  18. for(int j=min(i,k);j>=;j--)
  19. {
  20. for(int t=n;t>=a[i];t--)
  21. {
  22. dp[t][j]=(dp[t][j]+dp[t-a[i]][j-])%MOD;
  23. }
  24. }
  25. }
  26. int ans=;
  27. for(int i=;i<=k;i++) ans=(ans+dp[n][i])%MOD;
  28. printf("%d\n",ans);
  29. }
  30. return ;
  31. }

60分:我们考虑转移时优化一维——即把枚举数的ID这一维优化掉

我们对于转移进行分类讨论:

1.若转移前数列中没有1,那么我们就可以一次性往现数列中加1,并对新加上的1进行“不同化”——即对加进的t个1乘上C(4,t)(这样可以保证2,3,4……都已进行“不同化”)

2.若转移中有1,那么我们就把数列中所有数都加一,使其没有1

我们可以设$dp[i][j][bl]$为和为$i$,取了$j$个数,数列中是否含有1(这种设状态较好理解)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll MOD=;
  5.  
  6. ll dp[][][],n,k;
  7.  
  8. void add(ll &x,ll y)
  9. {
  10. x+=y;
  11. if(x>=MOD) x-=MOD;
  12. }
  13. ll c4[]={,,,,};
  14.  
  15. int main()
  16. {
  17. while(scanf("%lld%lld",&n,&k)!=EOF)
  18. {
  19. if(k>n) k=n;
  20. if(n==&&k==) break;
  21. memset(dp,,sizeof(dp));
  22. dp[][][]=;
  23. for(ll i=;i<=n;i++)
  24. {
  25. for(ll j=;j<=min(i,k);j++)
  26. {
  27. if(i>j)
  28. {
  29. add(dp[i][j][],dp[i-j][j][]);
  30. add(dp[i][j][],dp[i-j][j][]);
  31. }
  32. for(ll t=;t<=min(j,4LL);t++)
  33. add(dp[i][j][],dp[i-t][j-t][]*c4[t]%MOD);
  34. }
  35. }
  36. ll ans=;
  37. for(ll i=;i<=k;i++)
  38. {
  39. add(ans,dp[n][i][]);
  40. add(ans,dp[n][i][]);
  41. }
  42. printf("%lld\n",ans);
  43. }
  44. return ;
  45. }

当然,为了后面的满分代码更方便,我们考虑对状态进行降维,我们把最后bl去掉,把“所有数加1”和“往数组里加1”两个操作一起进行

考虑到满分是矩阵快速幂,我们把剩下的两位压进一维(k比较小,所以可以压)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll MOD=;
  5.  
  6. ll dp[],n,k;
  7.  
  8. void add(ll &x,ll y)
  9. {
  10. x+=y;
  11. if(x>=MOD) x-=MOD;
  12. }
  13. ll c4[]={,,,,};
  14. ll id(ll x,ll y)
  15. {
  16. return x*+y;
  17. }
  18.  
  19. int main()
  20. {
  21. while(scanf("%lld%lld",&n,&k)!=EOF)
  22. {
  23. if(k>n) k=n;
  24. if(n==&&k==) break;
  25. memset(dp,,sizeof(dp));
  26. dp[]=;
  27. for(ll i=;i<=n;i++)
  28. {
  29. for(ll j=;j<=min(i,k);j++)
  30. {
  31. for(ll t=;t<=min(j,4LL);t++)
  32. add(dp[id(i,j)],dp[id(i-j,j-t)]*c4[t]%MOD);
  33. //先让原数组j-t个数都加1,再加入t个1
  34. }
  35. }
  36. ll ans=;
  37. for(ll i=;i<=k;i++)
  38. {
  39. add(ans,dp[id(n,i)]);
  40. }
  41. printf("%lld\n",ans);
  42. }
  43. return ;
  44. }

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 $

代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const ll MOD=;
  5.  
  6. ll dp[][];
  7. int n,k;
  8.  
  9. void add(ll &x,ll y)
  10. {
  11. x+=y;
  12. if(x>=MOD) x-=MOD;
  13. }
  14. ll c4[]={,,,,};
  15. int id(int x,int y)
  16. {
  17. return (x-)*k+y;
  18. }
  19.  
  20. struct matrix{
  21. ll a[][];
  22. int n,m;
  23. matrix(){}
  24. matrix(int n,int m):n(n),m(m)
  25. {
  26. memset(a,,sizeof(a));
  27. }
  28. void print()
  29. {
  30. for(int i=;i<=n;i++)
  31. {
  32. for(int j=;j<=m;j++) printf("%lld ",a[i][j]);
  33. puts("");
  34. }
  35. }
  36. };
  37.  
  38. matrix operator * (matrix A,matrix B)
  39. {
  40. matrix C(A.n,B.m);
  41. int t=min(A.m,B.n);
  42. for(int i=;i<=C.n;i++)
  43. {
  44. for(int j=;j<=C.m;j++)
  45. {
  46. for(int p=;p<=t;p++)
  47. add(C.a[i][j],A.a[i][p]*B.a[p][j]%MOD);
  48. }
  49. }
  50. return C;
  51. }
  52.  
  53. matrix qpow(matrix B,matrix A,int y)
  54. {
  55. matrix Z=A;
  56. while(y)
  57. {
  58. if(y&) Z=Z*B;
  59. B=B*B;
  60. y>>=;
  61. }
  62. return Z;
  63. }
  64.  
  65. void ready()
  66. {
  67. memset(dp,,sizeof(dp));
  68. dp[][]=;
  69. for(int i=;i<=;i++)
  70. {
  71. for(int j=;j<=min(k,i);j++)
  72. {
  73. for(int t=;t<=min(j,);t++)
  74. add(dp[i][j],dp[i-j][j-t]*c4[t]%MOD);
  75. //先让原数组j-t个数都加1,再加入t个1
  76. }
  77. }
  78. }
  79.  
  80. void make_matrix()
  81. {
  82. matrix A(k*k,k*k);
  83. matrix B(k*k,k*k);
  84. for(int i=;i<=A.m;i++) A.a[i][i]=;
  85. for(int i=;i<=k*k-k;i++) B.a[i+k][i]=;
  86. for(int j=;j<=k;j++)
  87. {
  88. for(int t=;t<=min(,j-);t++) //和已经大于k,不可能再从取数为0的情况下一次转移而来
  89. //dp[2][1]就从dp[1][1]转移而来
  90. {
  91. B.a[id(k+-j,j-t)][k*k-k+j]=c4[t];
  92. }
  93. }
  94. matrix C(,k*k);
  95. for(int i=;i<=k;i++)
  96. {
  97. for(int j=;j<=k;j++)
  98. {
  99. C.a[][id(i,j)]=dp[i][j];
  100. }
  101. }
  102. B=qpow(B,A,n-k);
  103. C=C*B;
  104. ll ans=;
  105. for(int i=;i<=k;i++)
  106. {
  107. add(ans,C.a[][id(k,i)]);
  108. }
  109. printf("%lld\n",ans%MOD);
  110. }
  111.  
  112. int main()
  113. {
  114. while(scanf("%d%d",&n,&k)!=EOF)
  115. {
  116. if(k>n) k=n;
  117. if(n==&&k==) break;
  118. ready();
  119. if(n<=k)
  120. {
  121. ll ans=;
  122. for(int i=;i<=k;i++) add(ans,dp[n][i]);
  123. printf("%lld\n",ans);
  124. }
  125. else make_matrix();
  126. }
  127. return ;
  128. }

夏令营501-511NOIP训练18——高二学堂的更多相关文章

  1. XJOI 夏令营501-511NOIP训练18 高二学堂

    在美丽的中山纪念中学中,有座高二学堂,同样也是因为一个人,让它们变 成了现在这个样子~那就是我们伟大的级主任.因为他,我们又迎来了一个木有电影,只有对答案的段考日:又迎来了一个不是大礼拜,而是小礼拜的 ...

  2. test20190802 夏令营NOIP训练18

    今天的题很有难度啊.然而我10:40才看题-- 高一学堂 在美丽的中山纪念中学里面,有一座高一学堂.所谓山不在高,有仙则名:水不在深,有龙则灵.高一学堂,因为有了yxr,就成了现在这个样子 = =. ...

  3. 夏令营501-511NOIP训练18——高三楼

    传送门:QAQQAQ 题意:定义矩阵A与矩阵B重复,当且仅当A可以通过任意次行列交换得到B,例如下图A,B即为合法矩阵 现求对于$n*n$的矩阵有多少个不重复的矩阵 数据范围: 对于10%的数据 N≤ ...

  4. XJOI夏令营501训练1——分配工作

    传送门:QAQQAQ 题意:某公司有工作人员x1,x2,…,xn ,他们去做工作y1,y2,…,ym(n<=m) ,每个人都能做其中的几项工作,并且对每一项工作都有一个固定的效率.问能否找到一种 ...

  5. XJOI 夏令营501-511NOIP训练18 高三楼

    参观完各种饭堂,学校还有什么著名的景点呢?当然是教室了,此时此刻我 们来到了高三楼.你会发现高三楼门口会有以身份认证系统,这东西还有着一段疼人的历史.每年的九月到来,高三的童鞋大多不习惯学校的作息时间 ...

  6. 台州学院maximum cow训练记录

    前队名太过晦气,故启用最大牛 我们的组队大概就是18年初,组队阵容是17级生詹志龙.陶源和16级的黄睿博. 三人大学前均无接触过此类竞赛,队伍十分年轻.我可能是我们队最菜的,我只是知道的内容最多,靠我 ...

  7. Oracle汉字转拼音package

    --函数GetHzFullPY(string)用于获取汉字字符串的拼音 --select GetHzFullPY('中华人民共和国') from dual; --返回:ZhongHuaRenMinGo ...

  8. Linux运维之基础拾遗

    第一部分 Linux常用文件管理命令 1.1 cp 文件复制 常用选项 -i # 覆盖之前提醒用户确认 -f # 强制覆盖目标文件 -r # 递归复制目录 -d # 复制符号链接本身而非其指向的源文件 ...

  9. 获取文本的编码类型(from logparse)

    import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.F ...

随机推荐

  1. Shiro学习(12)与Spring集成

    Shiro的组件都是JavaBean/POJO式的组件,所以非常容易使用spring进行组件管理,可以非常方便的从ini配置迁移到Spring进行管理,且支持JavaSE应用及Web应用的集成. 在示 ...

  2. Android中的surfaceHolder.lockCanvas(null)返回为null详解

    对于新手学习SurfaceView的时候获取lockCanvas的时候总是返回null的问题很是纠结 canvas = surfaceHolder.lockCanvas(new Rect(0, 0, ...

  3. SVG和canvas

    1.SVG实现的圆环旋转效果 参考:http://www.softwhy.com/article-6472-1.html 2.SVG中的图形可以通过  transform="matrix(0 ...

  4. (转)oracle group by 和order by的关系(在一起使用注意事项)

    转:http://lzfhope.blog.163.com/blog/static/636399220092554045196/ 环境:oracle 10g单单group by 或者order by本 ...

  5. diji模板

    void diji(int x){ fill(dis,dis+n,INT_MAX); dis[x] = ; ;i < n;i++) pre[i] = i; ){ int minn = INT_M ...

  6. 6-23 EDM的报告

    EDM营销(Email Direct Marketing)也即:Email营销. 目的:数据分析.制定一对一的个性化数据.提高用户访问率.EDM是一对一的沟通,让你的用户感觉到尊重, 方式:选择强有力 ...

  7. 5. Jmeter常用快捷键

    快捷键 功能 备注 Ctrl + C 复制 可复制组件 Ctrl + V 粘贴 可粘贴组件 Ctrl + Shift + C 复制粘贴当前组件到下一行   Ctrl + R 运行测试计划   Ctrl ...

  8. 7、Appium常用API

    嗯,官网已经介绍的很全了.会选几个常用API后期整理. Appium常用API地址:http://appium.io/docs/cn/writing-running-appium/appium-bin ...

  9. 50-Ubuntu-其他命令-2-软硬链接示意图

    在Linux中,文件名和文件的数据是分开存储的. 使用ls -lh可以查看一个文件的硬链接数. 在日常工作中,几乎不会建立文件的硬链接数. 在Linux中,只有文件的硬链接数等于0才会被删除.如下图第 ...

  10. C语言结构体嵌套

    #include <stdio.h> int main() { /*************************************************** *结构体嵌套:结构 ...