zoukankan      html  css  js  c++  java
  • [BZOJ 2817] 波浪

    Link:

    BZOJ 2817 传送门

    Solution:

    算是比较神的DP了吧,

    首先这个绝对值处理起来很难受,肯定要想办法去掉

    于是想到从小到大插入的方式,便不存在绝对值的问题

    插入一个数只有5种情况,我们来分类讨论:

    1.插入以后它两边都没有数。(权值$-2*i$,方案数$k+1-l$)

    2.插入以后它两边都有数。(权值$2*i$,方案数$k-1$)

    3.插入以后它的一边有数。(权值$0$,方案数$k*2-l$)

    4.插入在边界上,且它旁边没有数。(权值$-i$,方案数$2-l$)

    5.插入在边界上,且它旁边有数。(权值$i$,方案数$2-l$)

    可以发现每种情况产生的权值都可以确定,且方案数也可以确定,于是想到便能使用DP转移

    设$f[i][j][k][l]$表示当前插入$i$个数,权值为$j$,序列被分为$k$段,序列两端状态为$l$时的方案数

    $l$表示序列两端是有0/1/2个数

    接下来就是巨恶心的高精度处理了:

    手写高精度速度肯定是不够的,

    于是只能用正式OI不给用的黑科技__float128了,(讲道理是不能用的,但不管A了就行)

    一篇介绍的文章 

    能保留小数点后32位,整数位能表示到1e4392?超乎我的想象

    (注意输出时要手动处理一下)

    但这还不够,$k<=8$时还是TLE,

    于是还要将输入分类,如果$k<=8$用double,否则才用__float128

    学会使用Template和namespace来进行不同处理

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define ll long long
    #define RG register
    const int M=4500;
    
    namespace db{double f[2][2*M+1][101][3];}
    namespace f128{__float128 f[2][2*M+1][101][3];}
    int n,m,k;
    
    template<class T> inline void print(T res)
    {
        printf("%d.",(int)res);  //逐位输出
        while(k--)
        {
            res=(res-(int)res)*10;
            if(!k) res+=0.5;
            printf("%d",(int)res);
        }
    }
    
    template<class T> inline void solve(T f[2][2*M+1][101][3])
    {
        f[1][M-1][1][1]=2;f[1][M-2][1][0]=1;f[0][M][1][2]=1;
        for(RG int i=2,cur=0,pre=1;i<=n;++i,pre=cur,cur^=1)
        {
            memset(f[cur],0,sizeof(f[cur]));
            for(RG int j=0;j<=2*M;++j)
                for(RG int k=1;k<=n-1;++k)
                    for(RG int l=0;l<=2;++l)
                    {
                        if(!f[pre][j][k][l]) continue;
                        if(j+2*i<=2*M) f[cur][j+2*i][k-1][l]+=f[pre][j][k][l]*(k-1);
                        if(j>=2*i) f[cur][j-2*i][k+1][l]+=f[pre][j][k][l]*(k+1-l);
                        f[cur][j][k][l]+=f[pre][j][k][l]*(k*2-l);
                        if(l<2)
                        {
                            if(j+i<=2*M) f[cur][j+i][k][l+1]+=f[pre][j][k][l]*(2-l);
                            if(j>=i) f[cur][j-i][k+1][l+1]+=f[pre][j][k][l]*(2-l);
                        }    
                    }
        }
        
        T res=0;
        for(RG int i=M+m;i<=2*M;++i) res+=f[n&1][i][1][2];
        for(RG int i=2;i<=n;++i) res/=i;
        print(res);
    }
    
    int main()
    {
        cin >> n >> m >> k;
        if(k<=8) solve(db::f);
        else solve(f128::f);
        return 0;
    }

    Review:

    1、通过排序的预处理规避绝对值运算

    2、DP时对边界条件加一个特殊状态

    3、对__float128的使用(输出要特殊处理)

    4、使用Template+namespace等方法提高代码重用性

  • 相关阅读:
    python3 使用代理
    oracle 删除重复记录
    oracle语句
    python 函数split()
    CentOS 6.6 中中文输入法设置
    CentOS 6.6 中设置Terminal快捷键
    vmware-workstation-11中centos-6.6安装
    利用powerdesigner反向数据库结构,生成ER图
    [转载]oracle 11g不能导出空表的多种解决方法
    [转载]新手入门:Spring的一些学习方法及意见
  • 原文地址:https://www.cnblogs.com/newera/p/9114022.html
Copyright © 2011-2022 走看看