CF596D Wilbur and Trees

题意:有一些高度为h的树在数轴上。每次选择剩下的树中最左边或是最右边的树推倒(各50%概率),往左倒有p的概率,往右倒1-p。

一棵树倒了,如果挨到的另一棵树与该数的距离严格小于h,那么它也会往同方向倒。

问所有树都被推倒后的期望覆盖长度?

n<=2000.

标程:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 const int inf=1e8+5;
 6 const int N=2005;
 7 int pos[N],h,n,L[N],R[N];
 8 double p,dp[N][N][2][2];
 9 int check(int op,int x,int dir)
10 {
11     if (op==0)
12     {
13         if (!dir) return min(pos[x]-pos[x-1],h);
14         else return min(h,max(pos[x]-pos[x-1]-h,0));
15     }else {
16         if (!dir) return min(h,max(pos[x+1]-pos[x]-h,0));
17         else return min(pos[x+1]-pos[x],h);
18     }
19 }
20 double dfs(int x,int y,int l,int r)
21 {
22     if (x>y) return 0;
23     if (dp[x][y][l][r]) return dp[x][y][l][r];
24     double &ans=dp[x][y][l][r];
25     //choose left
26     ans+=0.5*p*(dfs(x+1,y,0,r)+check(0,x,l));//fall left
27     if (R[x]+1<=y) ans+=0.5*(1-p)*(dfs(R[x]+1,y,1,r)+pos[R[x]]-pos[x]+h);//fall right
28       else ans+=0.5*(1-p)*(pos[y]-pos[x]+check(1,y,r));
29     //choose right
30     ans+=0.5*(1-p)*(dfs(x,y-1,l,1)+check(1,y,r));//fall right
31     if (x<=L[y]-1) ans+=0.5*p*(dfs(x,L[y]-1,l,0)+pos[y]-pos[L[y]]+h);//fall left
32       else ans+=0.5*p*(pos[y]-pos[x]+check(0,x,l));
33     return ans;
34 }
35 int main()
36 {
37     scanf("%d%d%lf",&n,&h,&p);
38     for (int i=1;i<=n;i++) scanf("%d",&pos[i]);
39     sort(pos+1,pos+n+1);pos[0]=pos[1]-h;pos[n+1]=pos[n]+h;
40     L[1]=1;R[n]=n;
41     for (int i=2;i<=n;i++) 
42       if (i==1||pos[i]-pos[i-1]<h) L[i]=L[i-1];else L[i]=i;
43     for (int i=n-1;i>=1;i--)
44       if (i==n||pos[i+1]-pos[i]<h) R[i]=R[i+1];else R[i]=i;
45     printf("%.10lf
",dfs(1,n,0,1));
46    return 0;
47 }

易错点:1.注意长度的判断。分类讨论。

2.需要设置极值端点,和1树、n树的距离要>=h。

题解:区间dp

剩下的一段树一定是连续的,区间dp即可。分四种情况讨论推导情况。

预处理一棵树往左/往右倒影响多少棵树。注意计算距离。