zoukankan      html  css  js  c++  java
  • Codeforces 1238E. Keyboard Purchase

    传送门

    注意到 $m$ 只有 $20$ ,考虑一下状压 $dp$

    设 $f[S]$ 表示当前确定的字符集合为 $S$ ,那么转移就考虑从最右边加入的下一个字符 $c$

    那么问题来了,代价如何计算

    考虑每次加入一个字符以后对于所有字符间的移动$(c_i,c_{i+1})$产生的代价

    那么显然只有当 $c_i in S$ ,$c_{i+1} otin S$ 时,移动 $(c_i,c_{i+1})$ 会多经过当前加入的字符的位置,那么需要的时间就会增加 $1$

    所以我们可以维护一个 $cnt[i][j]$ 表示 $c_{i}=i,c_{i+1}=j$ 的移动数量

    那么每次转移代价可以直接 $m^2$ 枚举所有 $c_{i} in S$ ,$c_{i+1} otin S$ 的移动求出

    这样复杂度是 $m^2 cdot 2^m$ 算一下发现达到了 $4 cdot 10^8$ 级别,时间限制 $1s$,显然很有问题

    那么现在有两种选择,优化算代价的速度,或者用信仰直接交复杂度不对的代码

    然后你发现信仰是对的,卡着时限是可以过的....比如这位神仙:https://codeforces.com/contest/1238/submission/62149786

    四个点 $997ms$ 一个点 $998ms$ $ ext{Orz}$

    但是我没有信仰

    来考虑一下如何快速计算代价,设 $h[S]$ 表示所有 $c_i in S$ ,$c_{i+1} otin S$ 的移动的数量

    再设 $g[p][S],p otin S$ 表示 $c_i=p , c_{i+1} in S$ 的移动的数量

    那么有 $h[S] = sum_{p in S} g[p][U ext{^}S]$ ,其中 $U$ 是全集

    现在考虑计算 $g$ ,如果对于每个 $p,S$ 都枚举所有 $p' otin S$ 再累加显然太慢了

    注意到 $S$ 的子集 $S'$ 的代价 $g[p][S']$ 一定会加入到 $g[p][S]$ 中,那么对于 $S$ 直接枚举某一位选择的 $p'$ 

    设 $S'|(1<<p')=S$,有 $g[p][S]=g[p][S']+cnt[p][p']$

    然后现在又有一个小问题,要对所有 $S$ 求出某一个为 $1$ 的位置

    考虑树状数组的操作, $x&-x$ 就是 $x$ 的第一位 $1$ 的值

    最后维护 $pos[x]$ 表示值为 $x=(1<<p)$ 时的 $p$

    然后这一题就解决了,复杂度 $O(m cdot 2^m)$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=1e5+7,M=20;
    int n,m,cnt[M][M];
    int pos[(1<<M)+7],f[(1<<M)+7],g[M][(1<<M)+7],h[(1<<M)+7];
    char s[N];
    int main() { n=read(),m=read(); scanf("%s",s+1); for(int i=1;i<n;i++) cnt[s[i]-'a'][s[i+1]-'a']++, cnt[s[i+1]-'a'][s[i]-'a']++; for(int i=0;i<m;i++) pos[1<<i]=i; memset(f,0x3f,sizeof(f)); int mx=(1<<m)-1; f[0]=0; for(int p=0;p<m;p++) for(int o=1;o<mx;o++) { if(o&(1<<p)) continue; g[p][o]=g[p][o-(o&-o)]+cnt[p][pos[o&-o]]; } for(int o=0;o<mx;o++) for(int p=0;p<m;p++) if(o&(1<<p)) h[o]+=g[p][mx^o]; for(int o=0;o<mx;o++) for(int p=0;p<m;p++) { if(o&(1<<p)) continue; f[o^(1<<p)]=min(f[o^(1<<p)],f[o]+h[o]); } printf("%d ",f[mx]); return 0; }
  • 相关阅读:
    glusterfs 术语
    python 随便
    ubuntu glusterfs 配置调试
    源码生成deb包
    常用apt cli
    unexpected error ConnectionError object has no attribute
    [MFC]透明图展示
    菜鸟的mongoDB学习---(六)MongoDB 索引
    Keyboard的显示与隐藏
    HDU 4268 Alice and Bob(贪心+Multiset的应用)
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11642956.html
Copyright © 2011-2022 走看看