Codeforces 题目传送门 & 洛谷题目传送门

人生中第一道 *3500(显然不是自己独立 AC 的),不过还是祭一下罢

神仙 D1F

首先考虑对于给定的序列 \(a_1,a_2,\dots,a_n\) 怎样求它的“密度”。假设其密度为 \(p\),那么对于全部 \(c^p\) 个子序列一定存在恰好 \(c^{p-1}\) 个以 \(1,2,\dots,c\) 开头的子序列。考虑 \(a\) 数组中最短的前缀 \(a[1...k]\),满足 \(a_1,a_2,\dots,a_k\) 中恰好包含了 \(1\sim c\) 中所有数,显然若不存在这样的前缀,则 \(p=0\)。根据 \(k\) 的定义可知 \(a_k\) 在 \(a[1...k]\) 中恰好出现了一次,我们考虑以 \(a_k\) 开头的 \(c^{p-1}\) 个子序列,显然对于它们在原序列中第一次出现的位置,设为 \(a_k,a_{d_1},a_{d_2},\dots,a_{d_{p-1}}\),必定有 \(d_1>k\),也就是说 \(a[k+1...n]\) 中必须包含全部长度为 \(p-1\) 的 \(c^{p-1}\) 个子序列。因此我们可以得到这样的引理:一个序列的密度,等于不断删除其一个包含 \(1\sim c\) 的前缀,最多的删除次数。(没想到 *1)

这样我们可以得到一个 \(dp\),\(dp_{i,j}\) 表示以 \(i\) 开头的子序列中,密度至少为 \(j\) 的子序列有多少个(没想到 *2),显然 \(dp_{i,0}=2^{n-i}\)。考虑怎样转移,我们枚举这样以 \(i\) 开头的子序列的 \(k\)(\(k\) 的含义见上文)在原序列中所对应的位置 \(r\),我们再记 \(f_{l,r}\) 为对于所有元素下标都 \(\in[l,r]\),其中 \(a_l,a_r\) 必须被选择的序列中,有多少个满足 \(1\sim c\) 全部出现,并且 \(a_r\) 恰好出现一次。那么显然有 \(dp_{i,j}=\sum\limits_{r=i}^nf_{i,r}\sum\limits_{t=r+1}^{n+1}dp_{t,j-1}\)(没想到 *3),后面那个东西显然可以用前缀和优化,因此我们现在只需求出 \(f_{i,r}\) 即可。考虑 \(f_{l,r}\) 怎么求,显然若 \(a_l=a_r\),那么 \(a_r\) 不可能在子序列中只出现一次,故 \(f_{l,r}=0\);若 \(a_l\ne a_r\),我们记 \(cnt_x\) 为 \(\sum\limits_{i=l+1}^{r-1}[a_i=x]\),即 \(x\) 在 \((l,r)\) 中的出现次数,对于 \(a_r\),显然只能在 \(a_r\) 中被选择一次,其他位置都不能被选择,方案数为 \(1\);对于 \(a_l\),显然它在 \((l,r)\) 中每一次出现都可以爱选不选,不选拉倒,反正 \(l\) 是必须要选的,不可能出现 \(a_l\) 在子序列中出现次数为 \(0\) 的情况,方案数 \(2^{cnt_{a_l}}\),对于其余所有 \(x\ne a_l\land x\ne a_r\),总共有 \(2^{cnt_{x}}\) 种选法,但由于出现次数不能为 \(0\),故需减去 \(1\),即 \(2^{cnt_x}-1\),用乘法原理将它们乘起来即可。这个可以通过预处理 \(2^k-1\) 及其逆元实现 \(\mathcal O(1)\) 维护,即边扫描边维护。(没想到 *4)。处理完 \(dp_{i,j}\),由于题目要求密度恰好为 \(k\) 的子序列个数,求个差分即可。

这样暴力复杂度是 \(n^3\) 的,不过有一个性质是这个密度不会超过 \(\lfloor\dfrac{n}{c}\rfloor\)(没想到 *5),因此你这个 \(j\) 只需处理到 \(\lfloor\dfrac{n}{c}\rfloor\),复杂度 \(\dfrac{n^3}{c}\)。

然而当 \(c\) 特别小的时候该算法还是过不去,我们考虑另一个 \(c\) 越小越好的暴力并对其进行数据分治(BJOI 既视感)(没想到 *6)。记 \(DP_{i,j,S}\) 表示考虑到前 \(i\) 位,现在已经删了 \(j\) 个恰好包含 \(1\sim c\) 所有数的前缀,当前前缀中包含了 \(S\) 中所有数的方案数,这个转移就比较容易了罢,分当前元素选和当前元素不选两种情况转移,如果当前元素不选,那就直接转移到 \(DP_{i+1,j,S}\),否则如果加上这个元素后 \(S\) 变成了 \(\{1,2,\dots,c\}\),那咱就多分一组,转移到 \(DP_{i+1,j+1,\varnothing}\),否则转移到 \(DP_{i+1,j,S\cup\{a_i\}}\),时间复杂度 \(n2^c\dfrac{n}{c}\)。取 \(c=11\) 作为两个暴力的分界点复杂度最优。

然鹅这样还是会被卡常,这时候就要拿出我们的终极卡常武器了。这里有一个比较实用的卡常技巧,由于此题模数是 \(998244353\),\(8\times 998244353\times 998244353\) 刚好在 long long 范围内,所以考虑开 long long 存 DP 数组,额外记录一个 \(cnt_{i,j}\) 表示 \(dp_{i,j}\) 距离上一次取模进行了几次加法,若 \(cnt_{i,j}\) 达到 \(8\) 就令 \(dp_{i,j}\leftarrow dp_{i,j}\bmod 998244353\),这样取模常数就变成了原来的 \(\dfrac{1}{8}\)(没想到 *7)。当然如果你还是过不去(虽然我没遇到这样的情况),据楼下 ymx 神仙所说,可以在 CF 上使用 GNU C++17 (64) 这门语言,这样 long long 的常数能小很多。

最后注意特判 \(c=1\)。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define fi first
  4. #define se second
  5. #define fill0(a) memset(a,0,sizeof(a))
  6. #define fill1(a) memset(a,-1,sizeof(a))
  7. #define fillbig(a) memset(a,63,sizeof(a))
  8. #define pb push_back
  9. #define ppb pop_back
  10. #define mp make_pair
  11. template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
  12. template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
  13. typedef pair<int,int> pii;
  14. typedef long long ll;
  15. typedef unsigned int u32;
  16. typedef unsigned long long u64;
  17. namespace fastio{
  18. #define FILE_SIZE 1<<23
  19. char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
  20. inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
  21. inline void putc(char x){(*p3++=x);}
  22. template<typename T> void read(T &x){
  23. x=0;char c=getchar();T neg=0;
  24. while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
  25. while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
  26. if(neg) x=(~x)+1;
  27. }
  28. template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
  29. template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
  30. void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
  31. }
  32. const int MAXN=3e3;
  33. const int MOD=998244353;
  34. int n,c,a[MAXN+5];
  35. int qpow(int x,int e=MOD-2){
  36. int ret=1;
  37. for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
  38. return ret;
  39. }
  40. namespace sub1{//subtask solving c>log2(n)
  41. int f[MAXN+5][MAXN+5],sum[MAXN+5][MAXN+5];ll dp[MAXN+5][MAXN+5];
  42. int pw2_1[MAXN+5],inv2_1[MAXN+5],cnt[MAXN+5];
  43. void solve(){
  44. for(int i=1;i<=n;i++) pw2_1[i]=(2*pw2_1[i-1]+1)%MOD;inv2_1[0]=1;
  45. for(int i=1;i<=n;i++) inv2_1[i]=qpow(pw2_1[i]);
  46. for(int l=1;l<=n;l++){
  47. int mul=1,col=1;memset(cnt,0,sizeof(cnt));
  48. cnt[a[l]]++;
  49. for(int r=l+1;r<=n;r++){
  50. if(a[r]==a[l]) mul=2ll*mul%MOD;
  51. else{
  52. mul=1ll*mul*inv2_1[cnt[a[r]]]%MOD;
  53. cnt[a[r]]++;if(cnt[a[r]]==1) col++;
  54. mul=1ll*mul*pw2_1[cnt[a[r]]]%MOD;
  55. }
  56. if(col==c&&(a[l]^a[r])) f[l][r]=1ll*mul*inv2_1[cnt[a[r]]]%MOD;
  57. }
  58. }
  59. for(int i=n+1;i;i--) dp[i][0]=pw2_1[n-i]+1;
  60. for(int i=n+1;i;i--) sum[i][0]=(sum[i+1][0]+dp[i][0])%MOD;
  61. for(int j=1;j<=n/c;j++){
  62. int cnt=0;
  63. for(int i=n;i;i--) for(int k=i+1;k<=n;k++,cnt++){
  64. dp[i][j]+=1ll*f[i][k]*sum[k+1][j-1];
  65. if(cnt>>3) dp[i][j]%=MOD,cnt=0;
  66. }
  67. for(int i=n;i;i--) sum[i][j]=(sum[i+1][j]+dp[i][j]%MOD)%MOD;
  68. }
  69. for(int i=0;i<=n;i++){
  70. if(!i) printf("%d ",(sum[1][i]-sum[1][i+1]+MOD-1)%MOD);
  71. else printf("%d ",(sum[1][i]-sum[1][i+1]+MOD)%MOD);
  72. }
  73. }
  74. }
  75. namespace sub2{//subtask solving c<=log2(n)
  76. const int MAXP=1<<11;
  77. int dp[2][MAXN+5][MAXP+5];
  78. void solve(){
  79. int pre=0,cur=1;dp[0][0][0]=1;
  80. for(int i=1;i<=n;i++){
  81. for(int j=0;j<=n/c;j++)
  82. for(int k=0;k<(1<<c)-1;k++) dp[cur][j][k]=0;
  83. for(int j=0;j<=n/c;j++){
  84. for(int k=0;k<(1<<c)-1;k++){
  85. dp[cur][j][k]=(dp[cur][j][k]+dp[pre][j][k])%MOD;
  86. if((k|(1<<a[i]-1))==(1<<c)-1) dp[cur][j+1][0]=(dp[cur][j+1][0]+dp[pre][j][k])%MOD;
  87. else dp[cur][j][k|(1<<a[i]-1)]=(dp[cur][j][k|(1<<a[i]-1)]+dp[pre][j][k])%MOD;
  88. }
  89. } pre^=cur^=pre^=cur;
  90. }
  91. for(int j=0;j<=n;j++){
  92. int ret=0;
  93. for(int k=0;k<(1<<c)-1;k++) ret=(ret+dp[pre][j][k])%MOD;
  94. if(!j) ret=(ret-1+MOD)%MOD;printf("%d ",ret);
  95. }
  96. }
  97. }
  98. namespace sub3{//subtask solving c=1
  99. int fac[MAXN+5],ifac[MAXN+5];
  100. void init_fac(int n){
  101. fac[0]=ifac[0]=ifac[1]=1;
  102. for(int i=2;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
  103. for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
  104. }
  105. int binom(int x,int y){return 1ll*fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;}
  106. void solve(){
  107. init_fac(n);printf("0 ");
  108. for(int i=1;i<=n;i++) printf("%d ",binom(n,i));
  109. }
  110. }
  111. int main(){
  112. scanf("%d%d",&n,&c);
  113. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  114. if(c>11) sub1::solve();
  115. else if(c>1) sub2::solve();
  116. else sub3::solve();
  117. return 0;
  118. }

Codeforces 1158F - Density of subarrays(dp,神仙题)的更多相关文章

  1. Codeforces & Atcoder神仙题做题记录

    鉴于Codeforces和atcoder上有很多神题,即使发呆了一整节数学课也是肝不出来,所以就记录一下. AGC033B LRUD Game 只要横坐标或者纵坐标超出范围就可以,所以我们只用看其中一 ...

  2. Codeforces 148D 一袋老鼠 Bag of mice | 概率DP 水题

    除非特别忙,我接下来会尽可能翻译我做的每道CF题的题面! Codeforces 148D 一袋老鼠 Bag of mice | 概率DP 水题 题面 胡小兔和司公子都认为对方是垃圾. 为了决出谁才是垃 ...

  3. Codeforces 464E The Classic Problem(主席树+最短路+哈希,神仙题)

    题目链接 题意:给出一张 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边连接 \(u_i,v_i\),边权为 \(2^{w_i}\),求 \(s\) 到 \(t\) 的最短路. \( ...

  4. [BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆)

    [BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆) 题面 一棵二叉树的所有点的点权都是给定的集合中的一个数. 让你求出1到m中所有权 ...

  5. CF1158F Density of subarrays

    CF1158F Density of subarrays 首先可以发现,有值的p最大是n/c 对于密度为p,每个数至少出现c次,且其实是每出现c个数,就分成一段,这样贪心就得到了p %ywy n/c ...

  6. DP刷题记录(持续更新)

    DP刷题记录 (本文例题目前大多数都选自算法竞赛进阶指南) TYVJ1071 求两个序列的最长公共上升子序列 设\(f_{i,j}\)表示a中的\(1-i\)与b中色\(1-j\)匹配时所能构成的以\ ...

  7. DP刷题记录

    目录 dp刷题记录 codeforces 706C codeforces 940E BZOJ3997 POJ2279 GYM102082B GYM102082D codeforces132C L3-0 ...

  8. 贪心/构造/DP 杂题选做Ⅱ

    由于换了台电脑,而我的贪心 & 构造能力依然很拉跨,所以决定再开一个坑( 前传: 贪心/构造/DP 杂题选做 u1s1 我预感还有Ⅲ(欸,这不是我在多项式Ⅱ中说过的原话吗) 24. P5912 ...

  9. 贪心/构造/DP 杂题选做Ⅲ

    颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...

随机推荐

  1. 【原创】Linux v4l2框架分析

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  2. logging模块二

    背景,在学习logging时总是遇到无法理解的问题,总结,尝试一下更清晰明了了,让我们开始吧! logging模块常用format格式说明 %(levelno)s: 打印日志级别的数值 %(level ...

  3. httpclient 登录成功后返回的cookie值访问下一页面

    HttpClient4.x可以自带维持会话功能,只要使用同一个HttpClient且未关闭连接,则可以使用相同会话来访问其他要求登录验证的服务(见TestLogin()方法中的"执行get请 ...

  4. 一个C#开发搭建Android框架的心路历程

    前言 Java框架实在是太多了,因为是初学乍练,所以,只好以百度为标准选择框架了. Java的框架文章太难写了,因为他引用了太多框架,而没一个框架都有很繁琐的配置,把每个框架都写一遍,就等于写书了:所 ...

  5. Hive计算最大连续登陆天数

    目录 一.背景 二.算法 1. 第一步:排序 2. 第二步:第二列与第三列做日期差值 3. 第三步:按第二列分组求和 4. 第四步:求最大次数 三.扩展(股票最大涨停天数) 强哥说他发现了财富密码,最 ...

  6. Kali安装Parallels Tools过程记录

    最近两天又参加了公司一年一度的网络安全劳动竞赛,之前用过的一个 Kali 忘记密码进不去了 -_- .重新安装了 Kali 2021.3a 之后发现 Parallels Tools 安装失败,记录了一 ...

  7. Windows内核中的CPU架构-7-陷阱门(32-Bit Trap Gate)

    Windows内核中的CPU架构-7-陷阱门(32-Bit Trap Gate) 陷阱门和中断门几乎是一模一样的: (注:图里高32位中的第11位的值为D,其实是1) 除了高32位中的type字段的内 ...

  8. 要web开发精品教程吗?免费无广告一百期连讲的那种-逐浪CMS前端开发100期入门教程全面开放

    要web开发精品教程吗?免费无广告一百期连讲的那种-逐浪CMS前端开发100期入门教程全面开放 大师主讲 经验难得 由逐浪CMS首席架构师发哥老师,亲自主理讲解. 历时一年精心打造, 汇聚了互联网诞生 ...

  9. Windows应用开发中程序窗口中的各种图标尺寸规划

    为了让你的图标在各个视图模式下都能有合适的尺寸,需要制作4种尺寸16x16.32x32.48x48.256x256 在Windows系统中,几乎所有窗口都是ListView,其中的图标都按照指定的尺寸 ...

  10. Django 小实例S1 简易学生选课管理系统 8 CSS样式优化

    Django 小实例S1 简易学生选课管理系统 第8节--CSS样式优化 点击查看教程总目录 作者自我介绍:b站小UP主,时常直播编程+红警三,python1对1辅导老师. 前面的几节下来,用户模块基 ...