2019HDU多校第六场1009 Three Investigators——杨表

Posted lfri

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019HDU多校第六场1009 Three Investigators——杨表相关的知识,希望对你有一定的参考价值。

题意

给定一个 n 个元素的数列,从前 k 个元素中取5次不下降子序列,求取得的和的最大值(k从1至n)

分析

       考虑将数字 a[i] 拆成 a[i] 个 a[i],比如 “4,1,2”→“4,4,4,4,1,2,2”,则问题转化为:找到最多 5 个不共享元素的不下降子序列,使得这些子序列包含的元素总量最多。可以证明,这等于杨氏图表前 5 层的长度之和。(手动模拟一下就能发现)

考虑杨氏图表求解答案的过程:

  • 从 1 到 n 依次考虑序列中的每个数,将其插入杨氏图表的第一层中。
  • 插入 x 时,如果 x 不小于这一层的最大的数,则将 x 放在这一层的末尾;否则找到大于 x 的最小的数 y,将 y 替换为 x,并将 y 插入下一层。

        因为每一层的元素都有序,所以可以用数组维护,寻找 y 的过程可以用二分查找加速。

        但是对于本题来说,我们不能暴力地插入 a[i] 个 a[i]。考虑将杨表每一层中相同的元素合 并,用 std::map 记录每个元素的个数,那么当我们一次性插入 x 个 x 时,只需要将其插入 std::map 中,然后不断消费后继,将后继的元素个数减少即可,在减少的时候要将其作为 “p 个 q” 插入下一层中。 每一类数字被消费完毕后需要及时从 std::map 中删除,而每次插入会导致最多一种其它 数字被拆分,所以每层的插入次数至多为上一层的两倍。

       假设要求不超过 k 个子序列的答案,本题中 k = 5,则时间复杂度为 $O(2^kn log n)$。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int K=5;
map<int, ll>T[K];
ll ans;
int n;

//在第o行插入p个x
void insert(int o,int x,ll p)
  if(o>=K)return;
  T[o][x]+=p;
  ans+=p;
  while(p)
    map<int,ll>::iterator it=T[o].lower_bound(x+1);
    if(it==T[o].end())return;
    ll t=min(p,it->second);
    ans-=t;
    p-=t;
    insert(o+1,it->first,t);
    if(t==it->second)T[o].erase(it);else it->second-=t;
  



int main()

    int t;
    scanf("%d", &t);
    while(t--)
    
        scanf("%d", &n);
        ans = 0;
        for(int i=0;i<K;i++) T[i].clear();
        for(int i = 1;i <= n;i++)
        
            int tmp;
            scanf("%d", &tmp);
            insert(0, tmp, tmp);
            printf("%lld%c", ans, i == n ? \n :  );
        
    
    return 0;

 

以上是关于2019HDU多校第六场1009 Three Investigators——杨表的主要内容,如果未能解决你的问题,请参考以下文章

hdu多校第六场1008 (hdu6641)TDL 暴力

多校第六场 1003 hdu 5355 Cake(贪心)

2020hdu多校第六场1006A Very Easy Graph Problem

2020 HDU 多校第六场 1010 Expectation 期望 矩阵树定理

2020 HDU 多校第六场 1010 Expectation 期望 矩阵树定理

杭电2018多校第六场(2018 Multi-University Training Contest 6) 1012.Pinball(HDU 6373) -简单的计算几何+物理受力分析