ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Luogu P2612 [ZJOI2012]波浪

2019-10-30 11:01:10  阅读:340  来源: 互联网

标签:联通 边界 int Luogu ZJOI2012 P2612 序列 一边 2i


题目
我们考虑从\(1\)到\(n\)把每个数放到序列里面去,以消掉绝对值。
在最后的序列中,如果\(i\)的某一边是序列的边界,那么\(i\)会产生\(0\)的贡献。如果\(i\)的某一边是一个比\(i\)小的数,那么\(i\)会产生\(i\)的贡献。如果\(i\)的某一边是一个比\(i\)大的数,那么\(i\)会产生\(-i\)的贡献。
当我们放\(i\)时,所有小于\(i\)的数都已经放进序列了,而大于\(i\)的还没放。
那么最后的序列中\(i\)的左右两边的情况就等同于放\(i\)的时候\(i\)两边是否紧贴着某个数。
所以我们可以把联通块(一个极大的连续的放了数的段)的情况和边界的情况放进dp的状态里。
设\(f_{i,j,k,l}\)表示放了\(i\)个数,序列中存在\(j\)个联通块,总贡献为\(k\)(这一维需要平移\(5000\)),有\(l\)个边界已经被紧贴(\(l=0,1,2\))的方案数。
发现第一维\(i\)显然是可以滚掉的。
那么有以下几个转移:
\(1.i\)一边是边界,一边是空:\(f_{j,k,l}(2-p)->f_{j+1,k-i,l+1}\ (p<2\wedge k\ge i)\)。
\(2.i\)一边是边界,一边是联通块:\(f_{j,k,l}(2-p)->f_{j,k+i,l+1}\ (p<2\wedge j\neq0\wedge k+i\le10000)\)。
\(3.\)两边都是联通块:\(f_{j,k,l}(j-1)->f_{j-1,k+2i,l}\ (j\ge 2\wedge k+2i\le10000)\)。
\(4.\)两边都是空:\(f_{j,k,l}(j+1-l)->f_{j+1,k-2i,l}\ (k\ge 2i)\)。
\(5.\)一边是联通块,一边是空:\(f_{j,k,l}(j*2-l)->f_{j,k,l}\ (j\neq0)\)。
那么最后的答案就是\(\frac{\sum\limits_{i=5000+m}^{10000}f_{1,i,2}}{n!}\)。
注意要数据类型分治。

#include<bits/stdc++.h>
using namespace std;
const int N=107,M=10007;
namespace db{long double f[2][N][M][3];}
namespace flt{__float128 f[2][N][M][3];}
int min(int a,int b){return a<b? a:b;}
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>void solve(T f[][N][M][3])
{
    T ans=0;
    f[0][0][5000][0]=1;
    int g=0,i,j,k,p;
    for(i=1;i<=n;++i)
    {
        g^=1,memset(f[g],0,sizeof f[g]);
        for(j=0;j<=min(i-1,m);++j)
            for(k=0;k<=10000;++k)
                for(p=0;p<=2;++p)
                    if(f[g^1][j][k][p])
            {
                        if(k>=2*i) f[g][j+1][k-2*i][p]+=f[g^1][j][k][p]*(j+1-p);
                        if(j) f[g][j][k][p]+=f[g^1][j][k][p]*(j*2-p);
                        if(j>=2&&k+2*i<=10000) f[g][j-1][k+2*i][p]+=f[g^1][j][k][p]*(j-1);
                        if(p^2)
            {
                            if(k>=i) f[g][j+1][k-i][p+1]+=f[g^1][j][k][p]*(2-p);
                            if(j&&k+i<=10000) f[g][j][k+i][p+1]+=f[g^1][j][k][p]*(2-p);
                        }
                    }
    }
    for(i=m;i<=5000;++i) ans+=f[g][1][5000+i][2];
    for(i=1;i<=n;++i) ans/=i;
    out(ans);
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    if(k<=8) solve(db::f);
    else solve(flt::f);
}

标签:联通,边界,int,Luogu,ZJOI2012,P2612,序列,一边,2i
来源: https://www.cnblogs.com/cjoierShiina-Mashiro/p/11763549.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有