• Luogu2612 ZJOI2012 波浪 DP


    传送门


    花掉了自己用来搞学科的时间做了这道题……

    一道类似的题:Here

    考虑拆开绝对值计算贡献。那么我们对于(1)(N)的排列,从小到大地将插入它们插入排列中。

    假设我们现在计算到了数(i),这意味着前(i-1)个数已经被插入到了排列中。考虑当前如何计算(i)的贡献。

    不难发现:在最终的排列中,(i)的贡献与它和前(i-1)个数和边界的相邻情况有关。如果(i)某一边与边界相邻,会产生(0)的贡献;某一边与小于(i)的数相邻,会产生(i)的贡献;某一边与大于(i)的数相邻,会产生(-i)的贡献。

    但是在这里大于(i)的数还没有被插入,所以这里必须要强制(i)与前(i-1)个数和边界的相邻情况才能够在当前阶段计算出(i)对序列价值的贡献。

    故设(f_{i,j,k,l})表示放完了前(i)个数、数列中存在(j)个连通块(定义连通块为一段极长区间,满足这一段区间任意的相邻的两个数都被强制定为相邻,也就是说在之后的转移中,这一段区间内不能插入数)、数列总价值为(k-5000)、有(l)个边界已经与某个数强制相邻的方案数。

    转移:

    (a.)一边与边界相邻,一边不与当前产生的连通块相邻,产生(-i)的价值,方案数为(2-l)

    (b.)一边与边界相邻,一边与当前产生的连通块相邻,产生(i)的价值,方案数为(2-l),要求(j eq 0)

    (c.)两边均与当前产生的连通块相邻,产生(2i)的价值,方案数为(j-1),要求(j geq 2)

    (d.)两边均不与当前产生的连通块相邻,产生(-2i)的价值,方案数为(j+1-l)

    (e.)一边与当前产生的连通块相邻,另一边不与当前产生的连通块相邻,产生(0)的价值,方案数为(j*2-l),要求(j eq 0)

    注意到(de)两种转移方案数都减去了(l),因为对于两端都不与边界相邻的连通块,可以选择左右之一与当前的数相邻,但是有一段与边界相邻的连通块只有一端可以。

    最后的答案就是(frac{sumlimits_{i=5000+M}^{10000} f_{N,1,i,2}}{n!})

    然后这鬼题还要数据类型分治……(K leq 8)使用long double,(K leq 30)使用__float128

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    //This code is written by Itst
    using namespace std;
    
    inline int read(){
        int a = 0;
        char c = getchar();
        bool f = 0;
        while(!isdigit(c)){
            if(c == '-')
                f = 1;
            c = getchar();
        }
        while(isdigit(c)){
            a = (a << 3) + (a << 1) + (c ^ '0');
            c = getchar();
        }
        return f ? -a : a;
    }
    
    namespace db{long double dp[2][110][10010][3];}
    namespace flt{__float128 dp[2][110][10010][3];}
    int N , M , K;
    
    template < class T >
    
    void out(T ans){
        cout << "0.";
        ans *= 10;
        for(int i = 1 ; i <= K ; ++i){
            cout << (int)(ans + (K == i) * 0.5);
            ans = (ans - (int)ans) * 10;
        }
    }
    
    template < class T >
    
    inline void solve(T dp[][110][10010][3]){
        T ans = 0;
        dp[0][0][5000][0] = 1;
        int now = 0;
        for(int i = 1 ; i <= N ; ++i){
            now ^= 1;
            memset(dp[now] , 0 , sizeof(dp[now]));
            for(int j = 0 ; j <= min(i - 1 , M) ; ++j)
                for(int k = 0 ; k <= 10000 ; ++k)
                    for(int p = 0 ; p <= 2 ; ++p)
                        if(dp[now ^ 1][j][k][p]){
                            if(k - 2 * i >= 0)
                                dp[now][j + 1][k - 2 * i][p] += dp[now ^ 1][j][k][p] * (j + 1 - p);
                            if(j)
                                dp[now][j][k][p] += dp[now ^ 1][j][k][p] * (j * 2 - p);
                            if(j >= 2 && k + 2 * i <= 10000)
                                dp[now][j - 1][k + 2 * i][p] += dp[now ^ 1][j][k][p] * (j - 1);
                            if(p != 2){
                                if(k - i >= 0)
                                    dp[now][j + 1][k - i][p + 1] += dp[now ^ 1][j][k][p] * (2 - p);
                                if(j && k + i <= 10000)
                                    dp[now][j][k + i][p + 1] += dp[now ^ 1][j][k][p] * (2 - p);
                            }
                        }
        }
        for(int i = M ; i <= 5000 ; ++i)
            ans += dp[now][1][5000 + i][2];
        for(int i = 1 ; i <= N ; ++i)
            ans /= i;
        out(ans);
    }
    
    int main(){
        #ifndef ONLINE_JUDGE
        //freopen("in" , "r" , stdin);
        //freopen("out" , "w" , stdout);
        #endif
        N = read();
        M = read();
        K = read();
        if(K <= 8)
            solve(db::dp);
        else
            solve(flt::dp);
        return 0;
    }
    
  • 相关阅读:
    javascript题目,如何在重写alert后还能正常弹出alert
    mass Framework support模块 v2
    javascript suggest效果
    HTML <div> 标签的 align 属性
    SQL GROUP BY 语句
    SQL UPDATE 语句
    HTML <font> 标签
    JavaScript eval() 函数
    HTML DOM Checkbox 对象
    CSS cursor 属性
  • 原文地址:https://www.cnblogs.com/Itst/p/10328072.html
Copyright © 2020-2023  润新知