题解 洛谷 P2612 【[ZJOI2012]波浪】DP+高精

题目描述

题目传送门

分析

因为有绝对值不好处理,所以我们强制从小到大填数

\(f[i][j][p][o]\) 为当前填到了第 \(i\) 个数,波动强度为 \(j\),有 \(p\) 个连续段并且两端的端点选了 \(o\) 个时的概率

注意这里的连续段是强制规定的

那么转移有五种:

\(1\)、填的数单独成为一段并且不在整个区间的两个端点上

\(f[i+1][j-i*2-2][p+1][o]+=f[i][j][p][o]*(p+1-o)\)

因为后面填在这个数两边的数一定比这个数大,所以这个数一定做负贡献

之前一共有 \(p\) 段,可以填的位置有 \(p+1\)

因为一个段如果在整个区间的两端的话,只能在它的一边填,还要减去 \(o\)

\(2\)、填的数单独成为一段并且在整个区间的两个端点上

\(f[i+1][j-i-1][p+1][o+1]+=f[i][j][p][o]*(2-o)\)

此时这个数只会做一次负贡献

方案数为剩余的整个区间的端点的数量,即 \(2-o\)

\(3\)、新填的数放在已有的连续段的一端并且不与其它连续段相邻

\(f[i+j][j][p][o]+=f[i][j][p][o]*(p*2-o)\)

因为这个数一定做一次正贡献和一次负贡献,所以会抵消,总贡献不变

每个连续段都有两个端点可以选,再减去已选整个区间的端点的数量

\(4\)、新填的数放在已有的连续段的一端并且与其它连续段相邻

\(f[i+1][j+2*i+2][p-1][o]+=f[i][j][p][o]*(p-1)\)

其实就是把两个端拼成了一个大段

做两次正贡献

\(5\)、新填的数放在已有的连续段的一端并且当前的数填到了整个区间的端点上

\(f[i+1][j+i+1][p][o+1]+=f[i][j][p][o]*(2-o)\)

做一次正贡献

因为题目要求保留的位数不一样

所以,对于精度较低的测试点,可以用 \(double\)

对于精度较高的测试点,我们可以手写高精度

手写高精度浮点数其实就是先把整个数左移很多位

然后进行正常的加、乘、除的操作

最后要多少位再四舍五入取到多少位即可

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#define rg register
const int maxn=105,maxm=8005,bas=2500,jz=10000000;
int n,m,k,mmax;
struct asd{
	int num[10],len;
	asd(){
		memset(num,0,sizeof(num));
		len=0;
	}
	friend asd operator +(const asd &A,const asd &B){
		asd C;
		C.len=std::max(A.len,B.len);
		for(rg int i=0;i<C.len;i++){
			C.num[i]+=A.num[i]+B.num[i];
			if(C.num[i]>=jz){
				C.num[i]-=jz;
				C.num[i+1]+=1;
			}
		}
		if(C.num[C.len]) C.len++;
		return C;
	}
	friend asd operator *(const asd &A,const asd &B){
		asd C;
		for(rg int i=0;i<B.len;i++){
			for(rg int j=0;j<A.len;j++){
				C.num[i+j]+=B.num[i]*A.num[j];
			}
		}
		for(rg int i=0;i<B.len;i++){
			for(rg int j=0;j<A.len;j++){
				if(C.num[i+j]>=jz){
					C.num[i+j+1]+=C.num[i+j]/jz;
					C.num[i+j]%=jz;
				}
			}
		}
		C.len=B.len+A.len;
		while(C.num[C.len-1]==0 && C.len-1!=0) C.len--;
		return C;
	}		
	friend asd operator /(const asd &A,const int B){
		asd C,D;
		D=A;
		for(rg int i=D.len-1;i>=0;i--){
			rg int haha=D.num[i]+D.num[i+1]*jz;
			C.num[i]=haha/B;
			D.num[i]=haha%B;
			D.num[i+1]=0;
			if(i==0 && D.num[i]>=B/2){
				C.num[i]++;
			}
			if(C.len==0 && C.num[i]){
				C.len=i+1;
			}
		}
		while(C.num[C.len-1]==0 && C.len-1!=0) C.len--;
		return C;
	}
	void read(int aa){
		len=0;
		while(aa){
			num[len++]=aa%jz;
			aa/=jz;
		}
		while(num[len-1]==0 && len-1!=0) len--;
	}
	void write(int aa){
		rg int pd=0;
		printf("0.");
		for(rg int i=len-1;i>=0;i--){
			if(aa-7>=0){
				if(aa==7 && num[i-1]%1000000%10>=5) pd=1;
				printf("%07d",num[i]+pd);
				aa-=7;
			} else {
				if(aa==1 && num[i]/100000%10>=5){
					pd=1;
				}
				if(aa){
					printf("%01d",num[i]/1000000%10+pd);
					aa--;
				}
				if(aa==1 && num[i]/10000%10>=5){
					pd=1;
				}
				if(aa){
					printf("%01d",num[i]/100000%10+pd);
					aa--;
				}
				if(aa==1 && num[i]/1000%10>=5){
					pd=1;
				}
				if(aa){
					printf("%01d",num[i]/10000%10+pd);
					aa--;
				}
				if(aa==1 && num[i]/100%10>=5){
					pd=1;
				}
				if(aa){
					printf("%01d",num[i]/1000%10+pd);
					aa--;
				}
				if(aa==1 && num[i]/10%10>=5){
					pd=1;
				}
				if(aa){
					printf("%01d",num[i]/100%10+pd);
					aa--;
				}
				if(aa==1 && num[i]%10>=5){
					pd=1;
				}
				if(aa){
					printf("%01d",num[i]/10%10+pd);
					aa--;
				}
				if(aa){
					printf("%01d",num[i]%10+pd);
					aa--;
				}
				break;
			}
		}
		printf("\n");
	}
}g[2][maxm][maxn/2][3];
asd anss;
void solve1(){
	mmax=n/2+1;
	rg int now=1;
	g[0][bas][0][0].num[6]=1;
	g[0][bas][0][0].len=7;
	for(rg int i=0;i<n;i++){
		now^=1;
		for(rg int j=0;j<maxm;j++){
			for(rg int p=0;p<=mmax;p++){
				for(rg int o=0;o<=2;o++){
					g[now^1][j][p][o].len=0;
					memset(g[now^1][j][p][o].num,0,sizeof(g[now^1][j][p][i].num));
				}
			}
		}
		asd haha;
		for(rg int j=0;j<maxm;j++){
			for(rg int p=0;p<=mmax;p++){
				for(rg int o=0;o<=2;o++){
					if(g[now][j][p][o].len){
						if(j-2*i-2>=0){
							haha.read(p+1-o);
							g[now^1][j-2*i-2][p+1][o]=g[now^1][j-2*i-2][p+1][o]+g[now][j][p][o]*haha/(i+1);
						}
						if(o<2 && j-i-1>=0){
							haha.read(2-o);
							g[now^1][j-i-1][p+1][o+1]=g[now^1][j-i-1][p+1][o+1]+g[now][j][p][o]*haha/(i+1);
						}
						haha.read(p*2-o);
						g[now^1][j][p][o]=g[now^1][j][p][o]+g[now][j][p][o]*haha/(i+1);
						if(p && j+2*i+2<maxm){
							haha.read(p-1);
							g[now^1][j+2*i+2][p-1][o]=g[now^1][j+2*i+2][p-1][o]+g[now][j][p][o]*haha/(i+1);
						}
						if(o<2 && j+i+1<maxm){
							haha.read(2-o);
							g[now^1][j+i+1][p][o+1]=g[now^1][j+i+1][p][o+1]+g[now][j][p][o]*haha/(i+1);
						}
					}
				}
			}
		}
	}
	for(rg int j=bas+m;j<=bas+(n+1)*n/2;j++){
		anss=anss+g[now^1][j][1][2];
	}
	anss.write(k);
}
double f[2][maxm][maxn/2][3],ans;
void solve2(){
	mmax=n/2+1;
	rg int now=1;
	f[0][bas][0][0]=1.0;
	for(rg int i=0;i<n;i++){
		now^=1;
		for(rg int j=0;j<maxm;j++){
			for(rg int p=0;p<=mmax;p++){
				for(rg int o=0;o<=2;o++){
					f[now^1][j][p][o]=0;
				}
			}
		}
		for(rg int j=0;j<maxm;j++){
			for(rg int p=0;p<=mmax;p++){
				for(rg int o=0;o<=2;o++){
					if(f[now][j][p][o]){
						if(j-2*i-2>=0) f[now^1][j-2*i-2][p+1][o]+=f[now][j][p][o]*(p+1-o)/(i+1);
						if(o<2 && j-i-1>=0) f[now^1][j-i-1][p+1][o+1]+=f[now][j][p][o]*(2-o)/(i+1);
						f[now^1][j][p][o]+=f[now][j][p][o]*(p*2-o)/(i+1);
						if(p && j+2*i+2<maxm)  f[now^1][j+2*i+2][p-1][o]+=f[now][j][p][o]*(p-1)/(i+1);
						if(o<2 && j+i+1<maxm) f[now^1][j+i+1][p][o+1]+=f[now][j][p][o]*(2-o)/(i+1);
					}
				}
			}
		}
	}
	for(rg int j=bas+m;j<=bas+(n+1)*n/2;j++){
		ans+=f[now^1][j][1][2];
	}
	std::cout<<std::fixed<<std::setprecision(k)<<ans<<std::endl;
}
int main(){
	std::cin>>n>>m>>k;
	if(k<=8) solve2();
	else solve1();
	return 0;
}

posted @ 2020-11-29 16:24  liuchanglc  阅读(256)  评论(4编辑  收藏  举报