• codeforces 938F(dp+高维前缀和)


    题意:

      给一个长度为n的字符串,定义$k=floor{log_2 n}$

      一共k轮操作,第i次操作要删除当前字符串恰好长度为$2^{i-1}$的子串

      问最后剩余的字符串字典序最小是多少?

    分析:

      首先很容易得到一个性质,那就是删除的那些串是可以不交叉的
      很容易想到一个很简单的dp

      dp[i][j]表示考虑原串的前i位,删除状态为j的情况下字典序最小的字符串(注意dp里面保存的是个字符串)

      那么很明显就是个O(n^3logn)的dp,无法通过

      dp里是一个字符串这个东西是很浪费时间而且很不优美的

      根据题解的做法,重新设计状态

      dp[i][j]表示已经确定了最终字符串的前i位,目前删除情况为j的情况下,字典序最小的字符串

      这样设计状态我们会发现一个性质,那就是如果dp[i][j]<dp[i][k],那么dp[i][k]就不起作用了

      所以dp数组可以用bool值来表示该状态是否为当前最小的字符串

      更新状态的话,根据确定位数i和删除位数j就知道那些"1"对应字符串的下一位是多少了,更新新的最小字符串

      然后我们要考虑当前阶段给后面要删除几个数,这里即使要求满足若一个状态的某个子集是真,那么它就是真

      这用一个高维前缀和解决即可

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=5000;
     4 char s[maxn+5];
     5 bool dp[maxn+5][maxn+5];
     6 int n,l,m;
     7 string ans;
     8 int main()
     9 {
    10     scanf("%s",s);
    11     n=strlen(s);
    12     l=0;
    13     while((1<<(l+1))<=n) ++l;
    14     m=1<<l;
    15     for(int j=0;j<m;++j)
    16     dp[0][j]=1;
    17     for(int i=1;i<=n-m+1;++i)
    18     {
    19         for(int j=0;j<m;++j) dp[i][j]=dp[i-1][j];
    20         char mi='z';
    21         for(int j=0;j<m;++j)
    22             if(dp[i-1][j]) mi=min(mi,s[i-1+j]);
    23         for(int j=0;j<m;++j)
    24             if(dp[i][j]&&s[i-1+j]!=mi) dp[i][j]=0;
    25 
    26         for(int j=0;j<m;++j)
    27             for(int k=0;k<l;++k)
    28                 if(j&(1<<k)) dp[i][j]|=dp[i][j^(1<<k)];
    29         ans=ans+mi;
    30 
    31     }
    32     cout<<ans<<endl;
    33 }
    View Code
  • 相关阅读:
    openwrt的内核版本是在哪个文件中指定的?
    git如何将一个分支合并到另一个分支?
    cygwin如何下编译安装tmux?
    如何合并ts文件?
    在cygwin下创建的文件位于windows的哪个目录下?
    linux shell的for循环语法是怎样的?
    内部类访问局部变量时,为什么需要加final关键字
    Java8函数式编程的宏观总结
    Maven私服使用经验总结
    java关于Integer设置-128到127的静态缓存
  • 原文地址:https://www.cnblogs.com/wmrv587/p/8531452.html
Copyright © 2020-2023  润新知