【BZOJ】2817: [ZJOI2012]波浪-DP

传送门:bzoj2817


题解

考虑拆去绝对值符号,而数列的相对大小波动如下:
【BZOJ】2817: [ZJOI2012]波浪-DP_第1张图片
发现只有转折点的数有贡献,可以套用两个套路:

  • 从小到大插入去掉绝对值的影响
  • D P DP DP只需要记录拆分成了几段(相对位置)

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示依次插入了 1 − i 1-i 1i,分成了 j j j段,差值之和为 k k k的方案数:

插入 i + 1 i+1 i+1时有以下情况:

  • 左右两边都有数, f [ i ] [ j ] [ k ] × ( j − 1 ) → f [ i + 1 ] [ j − 1 ] [ k + 2 ( i + 1 ) ] f[i][j][k]\times (j-1)\to f[i+1][j-1][k+2(i+1)] f[i][j][k]×(j1)f[i+1][j1][k+2(i+1)]
  • 只有一侧有数, f [ i ] [ j ] [ k ] × 2 j → f [ i + 1 ] [ j ] [ k ] f[i][j][k]\times 2j\to f[i+1][j][k] f[i][j][k]×2jf[i+1][j][k]
  • 两侧都没有数, f [ i ] [ j ] [ k ] × ( j + 1 ) → f [ i + 1 ] [ j + 1 ] [ k − 2 ( i + 1 ) ] f[i][j][k]\times (j+1)\to f[i+1][j+1][k-2(i+1)] f[i][j][k]×(j+1)f[i+1][j+1][k2(i+1)]

段覆盖左右端点时情况比较特殊,所以还需要加一维, f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f[i][j][k][l]表示左右端点有 l ( 0 ≤ l ≤ 2 ) l(0\leq l\leq 2) l(0l2)个被占用了的方案数。

卡精度+卡内存
分情况用__float128/double


代码

做了许久口胡选手,代码能力急剧下降

#include
using namespace std;
const int N=102,S=4500;
int n,m,K;
double f[2][N][S*2+5][3];
__float128 g[2][N][S*2+5][3];

template<class T>
inline void prit(T x)
{
    int nw=x;printf("%d.",nw);
    for(;K;--K){
        x=(x-nw)*10.0;
        if(K==1) x+=0.5;
        nw=x;printf("%d",nw);
    }
}

int main(){
    int i,j,k,t,pr=0;
    scanf("%d%d%d",&n,&m,&K);
    if(K>8){
        __float128 v,ans=0;g[0][0][S][0]=1;
        for(i=1;i<=n;++i){
            pr^=1;memset(g[pr],0,sizeof(g[pr]));
            for(j=0;j<=i;++j)
             for(k=0;k<=(S<<1);++k)
              for(t=0;t<=2;++t) if((v=g[pr^1][j][k][t])!=0){
                  if(t<2){
                      if(j) g[pr][j][k+i][t+1]+=v*(2-t);
                      g[pr][j+1][k-i][t+1]+=v*(2-t);
                  }
                  if(j>1) g[pr][j-1][k+(i<<1)][t]+=v*(j-1);
                  g[pr][j+1][k-(i<<1)][t]+=v*(j+1-t);
                  g[pr][j][k][t]+=v*((j<<1)-t);
              }
        }
         for(k=S+m;k<=(S<<1);++k)
          ans+=g[pr][1][k][2];
        for(i=2;i<=n;++i) ans/=i*1.0;
		prit(ans);
    }else{
        double v,ans=0;f[0][0][S][0]=1;
        for(i=1;i<=n;++i){
            pr^=1;memset(f[pr],0,sizeof(f[pr]));
            for(j=0;j<=i;++j)
             for(k=0;k<=(S<<1);++k)
              for(t=0;t<=2;++t) if((v=f[pr^1][j][k][t])!=0){
                  if(t<2){
                      if(j) f[pr][j][k+i][t+1]+=v*(2-t);
                      f[pr][j+1][k-i][t+1]+=v*(2-t);
                  }
                  if(j>1) f[pr][j-1][k+(i<<1)][t]+=v*(j-1);
                  f[pr][j+1][k-(i<<1)][t]+=v*(j+1-t);
                  if(j) f[pr][j][k][t]+=v*((j<<1)-t);
              }
        }
         for(k=S+m;k<=(S<<1);++k)
          ans+=f[pr][1][k][2];
		for(i=2;i<=n;++i) ans/=i*1.0;
		  prit(ans);
    }
    return 0;
}

你可能感兴趣的:(妙,计数DP)