1. 程式人生 > >【ZJOI2012】波浪【NOIP2017】赤壁情

【ZJOI2012】波浪【NOIP2017】赤壁情

Description

這裡寫圖片描述

Data Constraint

這裡寫圖片描述

Solution

先來一波套路:①從小到大插入能夠去掉絕對值的影響②dp只需要處理相對位置就可以記錄答案。
fi,j,k,l表示當前做到i,段數(連續有數的被稱作一段)為j,此時對答案貢獻為k,左右兩個邊界有多少個被填上,滿足這樣的狀態有多少種方案。
大致分為以下幾種情況:
①當前插入的值自成一段沒有貼邊界,貢獻為-2*i
②當前插入的值自成一段有貼邊界,貢獻為-i
③當前插入的值加入了別的段且不使兩段合併,貢獻為0
④當前插入的值加入了別的段且使兩段合併,貢獻為+2*i
在轉移的時候注意一下邊界問題。
因為精度要精確到30位,不想打小數高精度可以試試__float128黑科技,只是有點慢。

Code

#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<iostream>
#include<math.h>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
typedef double db;
typedef __float128 fl;
const int N=102,S=5000;
int n,m,o,i,j,k,l,Z;
db f[2
][102][S*2+5][3],tot; fl g[2][102][S*2+5][3],ans,TOT; void write(fl ans){ int tot=ans;printf("%d.",tot); while(o--){ ans=(ans-tot*1.0)*10.0; if(!o) ans=ans+0.5; tot=ans;printf("%d",tot); }printf("\n"); } int main(){ scanf("%d%d%d",&n,&m,&o); Z=0;f[0][0][S][0
]=g[0][0][S][0]=1; if(o<=8){ fo(i,1,n){ Z^=1;memset(f[Z],0,sizeof(f[Z])); fo(j,0,i){ fo(k,0,S*2){ fo(l,0,2){ tot=f[Z^1][j][k][l]; if(!tot) continue; if(l<2){ if(j) f[Z][j][k+i][l+1]+=tot*(2-l); f[Z][j+1][k-i][l+1]+=tot*(2-l); } f[Z][j+1][k-2*i][l]+=tot*(j+1-l); if(j) f[Z][j][k][l]+=tot*(2*j-l); if(j>1) f[Z][j-1][k+2*i][l]+=tot*(j-1); } } } }} else{ fo(i,1,n){ Z^=1;memset(g[Z],0,sizeof(g[Z])); fo(j,0,i){ fo(k,0,S*2){ fo(l,0,2){ TOT=g[Z^1][j][k][l]; if(!TOT) continue; if(l<2){ if(j) g[Z][j][k+i][l+1]+=TOT*(2-l); g[Z][j+1][k-i][l+1]+=TOT*(2-l); } g[Z][j+1][k-2*i][l]+=TOT*(j+1-l); if(j) g[Z][j][k][l]+=TOT*(2*j-l); if(j>1) g[Z][j-1][k+2*i][l]+=TOT*(j-1); } } } }} ans=0; if(o<=8){fo(i,S+m,2*S) ans+=f[Z][1][i][2];} else {fo(i,S+m,2*S) ans+=g[Z][1][i][2];} fo(i,1,n) ans/=i*1.0; write(ans); }