首页 > 其他分享 >P2612

P2612

时间:2023-11-14 18:12:07浏览次数:33  
标签:cnt int times P2612 一段 DP rightarrow

一道插入 DP 题。

分析

这一类型的 DP 大多是对许多段的维护,各个段之间的要求较弱或没有,一般都很难搞

先把概率转成计数。

观察题面,我们可以考虑维护一条从上到下类似扫描线的东西,每次计算下移一格的贡献。很明显,在坐标轴上画出图像,应当由数个峰组成。从上往下扫描就会形成一些联续段,考虑 DP 维护。

设 \(f_{i,j,k,p,q}\) 表示讨论了前 \(i\) 大的值,它们组成了 \(j\) 个连续段,已获得了 \(k\) 的贡献,左右两侧是否取满了。

讨论第 \(i\) 大放在了哪里,分类转移。

  1. 新增一段。\(f_{i,j,k,p,q}\times(j+1-p-q)\rightarrow f_{i+1,j+1,k+(2\times j-p-q),p,q}\)

  2. 新增靠边的一段。\(f_{i,j,k,p,q}\rightarrow f_{i+1,j+1,k+(2\times j-p-q),p+0/1,q+1/0}\)

  3. 增长一段。\(f_{i,j,k,p,q}\times(2\times j-p-q)\rightarrow f_{i+1,j,k+(2\times j-p-q),p,q}\)

  4. 合并两段。\(f_{i,j,k,p,q}\times(j-1)\rightarrow f_{i+1,j-1,k+(2\times j-p-q),p,q}\)

  5. 合并一段与边界。\(f_{i,j,k,p,q}\rightarrow f_{i+1,j,k+(2\times j-p-q),p+0/1,q+1/0}\)

运算过程注意精度,至于输出(借用了一下另一位的),从高到低判断应该输出什么即可。

#include<bits/stdc++.h>
using namespace std;
const int N=110,mod=1e9+7;
int n,m,q;
struct A{
	double f[N][N*N][2][2],g[N][N*N][2][2],ans;
	int main(){
		f[1][0][0][0]=f[1][0][0][1]=f[1][0][1][0]=f[1][0][1][1]=1;
		for(int i=2;i<=n;++i){
			for(int j=1;j<i;++j)
			for(int k=0;k<=i*i;++k)
			for(int p=0;p<2;++p)
			for(int q=0;q<2;++q){
				g[j][k][p][q]=f[j][k][p][q];
				f[j][k][p][q]=0;
			}
			for(int j=1;j<i;++j)
			for(int k=0;k<=i*i;++k)
			for(int p=0;p<2;++p)
			for(int q=0;q<2;++q){
				int cnt=k+2*j-p-q;
				double d=g[j][k][p][q];
				if(j+1-p-q>0) f[j+1][cnt][p][q]+=(j+1-p-q)*d/i;
				if(!p) f[j+1][cnt][1][q]+=d/i,f[j][cnt][1][q]+=d/i;
				if(!q) f[j+1][cnt][p][1]+=d/i,f[j][cnt][p][1]+=d/i;
				if(2*j-p-q>0) f[j][cnt][p][q]+=d*(2*j-p-q)/i;
				if(j>1) f[j-1][cnt][p][q]+=d*(j-1)/i;
			}
		}
		for(int i=m;i<=n*n;++i) ans+=f[1][i][1][1];
		if(ans + 1e-14 >= 1){cout << "1." << string(q,'0') << endl; return 0;}
		cout << "0.";
		ans *= 10;
		for(int i = 1 ; i <= q ; ++i){
			cout << (int)(ans + (q == i) * 0.5);
			ans = (ans - (int)ans) * 10;
		}
		return 0;
	}
}m1;
struct B{
	__float128 f[N][N*N][2][2],g[N][N*N][2][2],ans;
	int main(){
		f[1][0][0][0]=f[1][0][0][1]=f[1][0][1][0]=f[1][0][1][1]=1;
		for(int i=2;i<=n;++i){
			for(int j=1;j<i;++j)
			for(int k=0;k<=i*i;++k)
			for(int p=0;p<2;++p)
			for(int q=0;q<2;++q){
				g[j][k][p][q]=f[j][k][p][q];
				f[j][k][p][q]=0;
			}
			for(int j=1;j<i;++j)
			for(int k=0;k<=i*i;++k)
			for(int p=0;p<2;++p)
			for(int q=0;q<2;++q){
				int cnt=k+2*j-p-q;
				__float128 d=g[j][k][p][q];
				if(j+1-p-q>0) f[j+1][cnt][p][q]+=(j+1-p-q)*d/i;
				if(!p) f[j+1][cnt][1][q]+=d/i,f[j][cnt][1][q]+=d/i;
				if(!q) f[j+1][cnt][p][1]+=d/i,f[j][cnt][p][1]+=d/i;
				if(2*j-p-q>0) f[j][cnt][p][q]+=d*(2*j-p-q)/i;
				if(j>1) f[j-1][cnt][p][q]+=d*(j-1)/i;
			}
		}
		for(int i=m;i<=n*n;++i) ans+=f[1][i][1][1];
		if(ans + 1e-14 >= 1){cout << "1." << string(q,'0') << endl; return 0;}
		cout << "0.";
		ans *= 10;
		for(int i = 1 ; i <= q ; ++i){
			cout << (int)(ans + (q == i) * 0.5);
			ans = (ans - (int)ans) * 10;
		}
		return 0;
	}
}m2;
int main(){
	scanf("%d%d%d",&n,&m,&q);
	if(q<=8) m1.main();
	else m2.main();
	return 0;
}

标签:cnt,int,times,P2612,一段,DP,rightarrow
From: https://www.cnblogs.com/mRXxy0o0/p/17832225.html

相关文章