题目の传送门~

题目大意: 将\(n\)个蛋糕分成恰好\(k\)份, 求每份中包含的蛋糕的种类数之和的最大值.

这题有两种做法. 第一种是线段树优化dp, 我还没有考虑. 另一种就是分治+主席树.

然后如果看到分治+主席树的话 可以看成是题的二合一~

不过ADAMOLD正解应该是有\(O(nk)\)做法的吧, 我的\(O(nklogn)\)分治好像被卡了一点常数QwQ

首先我们可以非常容易的看出这题要用dp和状要用到的状态转移方程

\[f[i][j]=max{f[i-1][k]+d(k+1,j)}\ (k\in[1,j))
\]

那么很显然我们已经可以\(O(n^2k)\)做了. 但是这显然过不去, 我们必须要优化.

优化的方式有两种, 就是上面提到的线段树或者分治.

分治的话就是非常套路的东西了, 专门用来应对\(f[i][j]=f[i-1][k]+w(i,j)\), 其中费用函数\(w\)没啥特殊性质的情况的dp.

通常情况下, 我们观察每次要转移\(f[i][j]\)时令转移最优的\(A[i][j]\), 会发现有

\[A[i, j - 1] \leq A[i, j]\leq A[i + 1, j]
\]

啥意思呢就是这破玩意是单调的.所以我们假如我们求出\(f[i][mid]\)要在\(k\)处转移, 我们就知道\(f[i][1..mid-1]\)的转移点是在\((1,k)\)的了.

这样我们就有了一个分治的形式, 就可以直接做了. 而这类分治dp是有套路的(又到了py式伪代码时间

  1. def solve(x,l,r,L,R): # 处理f[x][l]..f[x][r]这一堆的dp值, 转移点落在[L,R]
  2. if l<=0 or l>r or r<=0 or r>n: # xjb写一通反正就是如果越界就不处理了
  3. return
  4. if x==1: # f[1]的情况作为边界条件显然要特殊处理.
  5. for i in range(l,r+1):
  6. f[x][i]=w(i,1)
  7. f[x][i]=INF # 这里的INF表示反向极限值(就是你要求min的话就是最大值
  8. g[x][i]=L
  9. for i in range(L,R+1):
  10. if f[x-1][i]+w(i+1,mid)>f[x][mid]: # 自然是对f[x][mid]进行转移啦~
  11. f[x][mid]=f[x-1][i]+w(i+1,mid) # 注意这里的i+1如果>mid的话要返回非法值(比如INF
  12. g[x][mid]=i # 标记最优的转移位置供继续分治使用
  13. solve(x,l,mid-1,L,g[x][mid]) # 递归处理左半边
  14. solve(x,mid+1,r,g[x][mid],R) # 递归处理右半边
  15. for i in range(1,k+1): # 第一维1~k都要做一遍..
  16. solve(i,1,n,1,n)

就可以啦, 每个题的区别就在求w(i,j)的部分了.

可以证明, 这个分治的过程每层是\(O(nlogn)\)的(反正窝不会证), 从1~k各扫一遍就是\(O(nklogn)\)的了.

对于ADAMOLD来说, 自然用\(O(n^2)\)预处理二维前缀和搞一下就ok了. (但是\(O(nklogn)\)有点卡常数?!

然后这个题的w(i,j)就是表示[i,j]区间内的蛋糕的种类数.

那么静态询问区间种类数的话我们就可以去看下DQUERY这道题咯(明显的模板题)

由于我并不认为这题可以离线, 所以树状数组或莫队是简明不行的. 我们要用主席树.

我们对每个时刻开一个\(n\)个节点的线段树, 然后用一个map记录每个数上一次出现的位置.

如果在第\(i\)个位置遇到一个没出现过的数\(x\), 我们把第\(i\)棵树的\(i\)位置+1.

如果遇到一个出现过的数\(y\), 我们先在第\(i\)棵树上把它上一次出现的位置\(last_y\)-1, 然后\(i\)位置+1,

这样就可以保证每个重复的数只存在于最后一次出现的位置,

这时候第\(i\)棵线段树就表示第\(i\)个时刻每个位置上不同的数的个数了.

我们可以画图来更好的体会这一点, 以DQUERY的样例为例,

5

1 1 2 1 3

首先我们要建一个有\(n\)个叶子节点的线段树(啊啊啊我画的图好丑啊 大家凑合看看, 意会一下?



我们在1位置遇到了一个1, 1还从来没有出现过, 我们让1 +1, 然后把\(last_1\)设成1.



我们在2位置又遇到了一个1, 1出现过了, 我们让\(last_1\)(1) -1, 然后让2 +1, 把\(last_1\)设成2.



我们在3位置遇到一个2, 2没出现过, 3 +1, \(last_2=3\)



我们在4位置又双叒叕遇到一个1, \(last_1\)(2) -1, 4 +1,\(last_1=4\)



5位置遇到一个3, 5 +1, \(last_3=5\)



这样我们就建好静态的主席树了, 我们看一下, 是不是第\(i\)棵树对应着第\(i\)个时刻的种类情况呢~

这样我们查询\([L,R]\)这个区间的时候, 就只需要在第\(R\)棵树上查\(L\)点及以后的和就完了OwO

单次查询都是\(O(logn)\)的

然后我们就解决了分治时候算\(w(i,j)\)的问题, 现在的时间复杂度应该是\(O(nklogn)*O(logn)=O(nklog^2n)\)的.

可以通过本题了, 虽然复杂度比线段树优化的\(O(nklogn)\)要多个log, 但是实际情况并没有慢太多(都是朴素实现大约慢一倍?

不过(可能?)要好写一点..

代码:

  1. #include <cstdio>
  2. #include <unordered_map>
  3. std::unordered_map<int, int> mp;
  4. const int N=36005;
  5. struct node{int sum,l,r;}t[N<<5];
  6. int f[55][N],g[55][N],rt[N],n,k,tot;
  7. inline int gn(int a=0,char c=0){
  8. for(;c<'0'||c>'9';c=getchar());
  9. for(;c>47&&c<58;c=getchar()) a=a*10+c-48;
  10. return a;}
  11. void update(int &x,int l,int r,int pre,int pos,int val){
  12. t[++tot]=t[pre]; x=tot; t[x].sum+=val;
  13. if(l==r) return; int mid=(l+r)>>1;
  14. if(pos<=mid) ::update(t[x].l, l, mid, t[pre].l, pos, val);
  15. else ::update(t[x].r, mid+1, r, t[pre].r, pos, val);
  16. }
  17. int query(int x,int l,int r,int L){
  18. if(L<=l) return t[x].sum;
  19. int mid=(l+r)>>1;
  20. if(L<=mid) return ::query(t[x].l, l, mid, L)+t[t[x].r].sum;
  21. return ::query(t[x].r, mid+1, r, L);
  22. }
  23. inline int qquery(int l,int r){
  24. if(l>r) return 0x7fffffff;
  25. return ::query(rt[r], 1, n, l);
  26. }
  27. void solve(int x,int l,int r,int L,int R){
  28. if(l<=0||l>r||r<=0||r>n) return;
  29. if(x==1){
  30. for(int i=l;i<=r;++i)
  31. f[x][i]=::qquery(1, i);
  32. return;
  33. } int mid=(l+r)>>1,maxn=0;
  34. f[x][mid]=0;; g[x][mid]=L;
  35. for(int i=L;i<=R;++i){
  36. maxn=f[x-1][i]+::qquery(i+1, mid);
  37. if(maxn>f[x][mid])
  38. f[x][mid]=maxn,g[x][mid]=i;
  39. }
  40. solve(x,l,mid-1,L,g[x][mid]);
  41. solve(x,mid+1,r,g[x][mid],R);
  42. }
  43. int main(){ n=gn(); k=gn(); int tmp;
  44. for(int i=1;i<=n;++i){
  45. int x=gn();
  46. if(mp.find(x)==mp.end())
  47. ::update(rt[i], 1, n, rt[i-1], i, 1);
  48. else{
  49. ::update(tmp, 1, n, rt[i-1], mp[x], -1);
  50. ::update(rt[i], 1, n, tmp, i, 1);
  51. }
  52. mp[x]=i;
  53. }
  54. for(int i=1;i<=k;++i)
  55. ::solve(i, 1, n, 1, n);
  56. printf("%d",f[k][n]);
  57. }

对 就是这样咯~

【学术篇】CF833B TheBakery 分治dp+主席树的更多相关文章

  1. 【学术篇】SPOJ COT 树上主席树

    这是学完主席树去写的第二道题_(:з」∠)_ 之前用树上莫队水过了COT2... 其实COT也可以用树上莫队水过去不过好像复杂度要带个log还是怎么样可能会被卡常数.. 那就orz主席吧.... 写了 ...

  2. SRM12 T2夏令营(分治优化DP+主席树 (已更新NKlogN)/ 线段树优化DP)

     先写出朴素的DP方程f[i][j]=f[k][j-1]+h[k+1][i] {k<i}(h表示[k+1,j]有几个不同的数)  显然时间空间复杂度都无法承受   仔细想想可以发现对于一个点 i ...

  3. BZOJ 4367 [IOI2014]holiday (决策单调DP+主席树+分治)

    题目大意:略 题目传送门 神题,不写长题解简直是浪费了这道题 贪心 考虑从0节点出发的情况,显然一直往前走不回头才是最优策略 如果起点是在中间某个节点$s$,容易想到,如果既要游览$s$左边的某些景点 ...

  4. Luogu4755 Beautiful Pair 最值分治、主席树

    传送门 整天做一些模板题感觉药丸 设\(val_i\)表示第\(i\)个位置的值 看到区间最大值考虑最值分治.对于当前的区间\([l,r]\),找到区间最大值\(mid\),递归\([l,mid-1] ...

  5. luogu P5892 [IOI2014]holiday 假期 决策单调性优化dp 主席树

    LINK:holiday 考虑第一个subtask. 容易想到n^2暴力枚举之后再暴力计算答案. 第二个subtask 暴力枚举终点可以利用主席树快速统计答案. 第三个subtask 暴力枚举两端利用 ...

  6. 【STSRM12】夏令营(分治决策单调+主席树)

    [题意]n个数字分成k段,每一段的价值是段内不同数字的个数,求最大价值.n<=35000,k<=50. [算法]分治决策单调+主席树(可持久化线段树) [题解] f[i][j]表示前i天分 ...

  7. Codeforces 833B The Bakery(主席树 + 决策单调性优化DP)

    题目链接 The Bakery 题目大意:目标是把$n$个数分成$k$组,每个组的值为这个组内不同的数的个数,求$k$个组的值的和的最大值. 题目分析: 这道题我的解法可能和大众解法不太一样……我用主 ...

  8. 5.15 牛客挑战赛40 E 小V和gcd树 树链剖分 主席树 树状数组 根号分治

    LINK:小V和gcd树 时限是8s 所以当时好多nq的暴力都能跑过. 考虑每次询问暴力 跳父亲 这样是nq的 4e8左右 随便过. 不过每次跳到某个点的时候需要得到边权 如果直接暴力gcd的话 nq ...

  9. 洛谷P3248 树 [HNOI2016] 主席树+倍增+分治

    正解:主席树+倍增+分治 解题报告: 传送门! 首先看到这题会想到之前考过的这题 但是那题其实简单一些,,,因为那题只要用个分治+预处理就好,只是有点儿思维难度而已 这题就不一样,因为它说了是按照原树 ...

随机推荐

  1. onLaunch与onLoad同步获取用户数据

    前言 在开发项目的时候遇到从全局获取用户信息,逻辑是从app.js中的onLauch获取,page页面的onLoad拿到数据填充到页面.遇到的问题是onLauch与onLoad是异步的,没办法从页面判 ...

  2. 四、附加到进程调试(.NET Framework)

    附加到进程调试: 1.需要在IIS配置环境并可运行即通过浏览器可打开. 2.找到项目w3wp.exe进程并附加到进程调试,点击项目添加断点,直接访问浏览器即可. 优点:w3wp.exe是已经运行的,调 ...

  3. dictionary小项目代码管理

    软件项目开发流程 需求分析 ----> 概要设计  ---> 项目计划 ---->详细设计--->编码测试 -----> 项目测试 ---->调试修改 ---> ...

  4. 力扣—set matrix zeroes (矩阵置零) python实现

    题目描述: 中文: 给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0.请使用原地算法. 英文: Given a m x n matrix, if an eleme ...

  5. js实用小函数收集

      格式化金额 var val='212312.235423' var rex = /\d{1,3}(?=(\d{3})+$)/g; val.replace(/^(-?)(\d+)((\.\d+)?) ...

  6. Java中"String.equals()“和"=="的区别

    Do NOT use the `==`` operator to test whether two strings are equal! It only determines whether or n ...

  7. Halo(三)

    接口中可以定义方法 1. 定义静态方法(直接调用) public interface Test { public static void method() { /** * 1.定义一个静态的带有方法体 ...

  8. tomcat启动、停止和重启脚本

    脚本名称:r.sh 脚本用途:启动.停止和重启tomcat 脚本参数:$1:[start|stop|restart] #!/bin/bash BIN_PATH="/tomcat_path/b ...

  9. map-DBA-comands

  10. ABP的新旧版本

    新版本 https://abp.io/documents/abp/latest/Index https://github.com/abpframework/abp ABP is an open sou ...