zoukankan      html  css  js  c++  java
  • 【洛谷2612】[ZJOI2012] 波浪(DP+卡精度)

    点此看题面

    • 定义一个序列的波动值为每相邻两项差的绝对值之和。
    • 求一个长度为(n)的序列波动值大于等于(m)的概率,保留(d)位小数。
    • (nle100,kle30)

    动态规划

    绝对值一看就非常麻烦,所以套路地想到从小到大枚举每一个数把它插入序列中,这样就能保证每次插入的数一定大于序列中已有的所有数,小于序列中没有的所有数。而大小关系一旦确定,绝对值也自然可以除去了。

    考虑一个数(i)的贡献,分别考虑两侧,如果是边界则没有贡献,否则若(i)更大则产生(i)的贡献,若(i)更小则产生(-i)的贡献。

    若把(i)插在序列中间,可以在插入时钦定它两侧的贡献。如果某侧是(i)说明这个空隙中不再插入数,如果某侧是(-i)说明这个空隙中必须插入数。因此我们可以记一下必须插入数的空隙个数(k),则把(i)插在序列中间的方案数其实也就是(k)

    若把(i)插在序列边上,两侧贡献的讨论和上面是一样的,但一个需要额外考虑的问题是它是否为最终的端点(如果是则边界侧贡献为(0),如果不是则边界侧必然需要插入数贡献只能为(-i))。因此我们还需要记录已确定的最终端点个数(x),则把(i)插在序列边上的方案数应该是(2-x)

    综上,设(f_{i,j,k,x})表示当前要插入(i),贡献总和为(j)(为了防止出现负数,给它加上(5000)),必须插入数的空隙个数为(k),已确定端点个数为(x)时的方案数。

    初始状态只需考虑(1)是否为最终端点。

    卡精度

    本意应该是想让我们写高精度,懒了点直接用__float128

    然而全部开__float128大数据会T掉,因此需要特判(dle 8)的点开long double

    代码:(O(n^4))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define V 5000
    using namespace std;
    int n,m,d;long double f1[2][2*V+5][N+5][3];__float128 f2[2][2*V+5][N+5][3];
    Tp I void T(Ty x)//保留d位小数
    {
    	if(!d) return (void)printf("%d
    ",round((double)x));printf("%d.",(int)x),x-=(int)x;//输出整数部分(特判d=0)
    	for(RI i=1;i^d;++i) printf("%d",(int)(x*=10)),x-=(int)x;printf("%d
    ",(int)round((double)(x*=10)));//直接输出前d-1位,四舍五入输出第d位
    }
    Tp I void Solve(Ty f[2][2*V+5][N+5][3])//动态规划
    {
    	RI i,j,k,x;Ty v,nv;for(f[1][V-2][0][0]=1,f[1][V-1][0][1]=2,i=2;i<=n;++i)//初始化考虑1是否为最终端点
    	{
    		for(j=0;j<=2*V;++j) for(k=0;k^i;++k) for(x=0;x<=2;++x) f[i&1][j][k][x]=0;//清空
    		#define DP(_j,_k,_x) (_j>=0&&_j<=2*V&&(f[i&1][_j][_k][_x]+=nv))//转移,防数组越界
    		for(j=0;j<=2*V;++j) for(k=0;k^(i-1);++k) for(x=0;x<=2;++x) if(v=f[i&1^1][j][k][x]/i)
    			(nv=k*v)&&(DP(j-2*i,k+1,x),DP(j,k,x),DP(j,k,x),DP(j+2*i,k-1,x)),//插在序列中间,钦定两侧贡献
    			(nv=(2-x)*v)&&(DP(j,k,x),DP(j-2*i,k+1,x),DP(j+i,k,x+1),DP(j-i,k+1,x+1));//插在序列边上,讨论是否为最终端点,再钦定非边界侧贡献
    	}
    	Ty t=0;for(i=m;i<=V;++i) t+=f[n&1][V+i][0][2];T(t);//统计最终贡献和大于等于m的概率之和
    }
    int main()
    {
    	if(scanf("%d%d%d",&n,&m,&d),n==1) return T(m?0:1),0;if(m>V) return T(0),0;//特判n=1和m过大
    	return d<=8?Solve(f1):Solve(f2),0;//数据分治
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    【pandas】'Styler' object has no attribute 'highlight_between'
    【原创】3行代码搞定:Python批量实现多Excel多Sheet合并
    【挑战阿里面试题-10种方法实现DataFrame转list】
    SpringCloud+RocketMQ实现分布式事务
    分布式事物SAGA
    分布式事务TCC
    多线程学习——思维导图
    .NET CLI简单教程和项目结构
    使用Google Fonts注意事项
    如何在印刷品中使用遵循SIL Open Font License协议的字体
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2612.html
Copyright © 2011-2022 走看看