Codeforces #282 div 1 C Helping People 题解

CF 282 C Helping People 题解

原题

time limit per test
2 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Malek is a rich man. He also is very generous. That's why he decided to split his money between poor people. A charity institute knows npoor people numbered from 1 to n. The institute gave Malek q recommendations. A recommendation is a segment of people like [l, r]which means the institute recommended that Malek gives one dollar to every person whose number is in this segment.

However this charity has very odd rules about the recommendations. Because of those rules the recommendations are given in such a way that for every two recommendation [a, b] and [c, d] one of the following conditions holds:

  • The two segments are completely disjoint. More formally either a ≤ b < c ≤ d or c ≤ d < a ≤ b
  • One of the two segments are inside another. More formally either a ≤ c ≤ d ≤ b or c ≤ a ≤ b ≤ d.

The goodness of a charity is the value of maximum money a person has after Malek finishes giving his money. The institute knows for each recommendation what is the probability that Malek will accept it. They want to know the expected value of goodness of this charity. So they asked you for help.

You have been given the list of recommendations and for each recommendation the probability of it being accepted by Malek. You have also been given how much money each person initially has. You must find the expected value of goodness.

Input

In the first line two space-separated integers n, q (1 ≤ n ≤ 1051 ≤ q ≤ 5000) are given.

In the second line n space-separated integers a1, a2, ..., an (0 ≤ ai ≤ 109) are given meaning that person number i initially has ai dollars.

Each of the next q lines contains three space-separated numbers li, ri, pi (1 ≤ li ≤ ri ≤ n0 ≤ p ≤ 1) where li and ri are two integers describing the segment of recommendation and pi is a real number given with exactly three digits after decimal point which is equal to probability of Malek accepting this recommendation.

Note that a segment may appear several times in recommendations.

Output

Output the sought value. Your answer will be considered correct if its absolute or relative error is less than 10 - 6.

Sample test(s)
input
5 2
1 7 2 4 3
1 3 0.500
2 2 0.500
output
8.000000000
input
5 2
281 280 279 278 282
1 4 1.000
1 4 0.000
output
282.000000000
input
3 5
1 2 3
1 3 0.500
2 2 0.250
1 2 0.800
1 1 0.120
2 2 0.900
output
4.465000000

废话】好久没写博客了。(我不会告诉你我是离线写的)于是来水经验来了。

来源简述】CF 282 C

原题简述】有N(10^5)个人,每个人有初始的钱。再给出M(5000)个操作L,R,P。每次表示L~R这些人有几率P(0<=P<=1)给他们每人一元。求最后所有人钱数最大值的期望。

算法简述】首先把这些操作建立出树结构(可以借鉴线段树)。节点i表示范围Li~Ri,它的父亲一定包含它,它也包含它的所有子树。为了方便,建立一个L=1,R=N,P=0的无效节点作为根。

观察到M的范围小,我们用f[i][j]表示在节点i表示的范围内,加的钱数<=j的期望(注意原先的钱数可以用RMQ计算出)。至于为什么是<=,因为后面要用到前缀和——反正f算的时候再前缀和一下。那么到节点i,我们开一数组tmp[j]表示所有子树中影的最多(注意还是前缀和性质)加了j元的期望。

那么tmp[j]= ∏f[son][mx[i]+j-mx[son]];

mx[o]是原先区间o的最大钱数。

(这里就用到了f的前缀和性质了)

注意到求完后做一步tmp[j]-=tmp[j-1],取消前缀和性质。

然后我们的任务是求出i的所有f值。

那么ans[i][j]=ans[i][j-1]+tmp[j-1]*p[i]+tmp[j]*(1-p[i]);

    ans[i][j-1]:前缀和

    tmp[j-1]*p[i]:由子树中得最大加j-1,且当前也加

tmp[j]*(1-p[i]):由子树中得最大加j,且当前不加

求完了所有的f[i][j]后,我们对于新加的点K,最后的ans满足

ANS=ans[m][0]*mx[m]+Σ (ans[m][i]-ans[m][i-1])*(mx[m]+i);

【*精华所得类似于分治的树形算法。

【代码】

#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100005
#define M 5005
using namespace std;
struct arr{int l,r;double p;}a[M];
int f[N][18],mx[N],used[N],n,i,j,T,m,k;
double ans[M][M],tmp[M],ANS;
inline int ask(int x,int y)
{
  int len=(int)log2(y-x+1);
  return max(f[x][len],f[y-(1<<len)+1][len]);
}
inline int cmp(const arr &a,const arr &b){return a.r-a.l<b.r-b.l;}
int main()
{
  scanf("%d%d",&n,&m);
  for (i=1;i<=n;i++) scanf("%d",&f[i][0]);
  for (j=1;j<=17;j++)
    for (i=1;i<=n;i++)
      T=i+(1<<(j-1)),f[i][j]=max(f[i][j-1],(T<=n)?f[T][j-1]:0);
  for (i=1;i<=m;i++)
    scanf("%d%d%lf",&a[i].l,&a[i].r,&a[i].p);
  a[++m]=(arr){1,n,0};
  sort(a+1,a+m+1,cmp);
  for (i=1;i<=m;i++)
  {
    mx[i]=ask(a[i].l,a[i].r);
    for (k=0;k<=m;k++) tmp[k]=1.0;
    for (j=1;j<i;j++) 
      if (a[j].l>=a[i].l&&a[j].r<=a[i].r&&!used[j])
      {
        used[j]=1;
        for (k=0;k<=m;k++)
          if (mx[i]+k-mx[j]<=m) tmp[k]*=ans[j][mx[i]+k-mx[j]];
      }
    for (k=m;k;k--) 
      tmp[k]-=tmp[k-1];
    ans[i][0]=(1-a[i].p)*tmp[0];
    for (k=1;k<=m;k++) 
      ans[i][k]=ans[i][k-1]+tmp[k-1]*a[i].p+tmp[k]*(1-a[i].p);
    //ans[i][k-1]:加上k-1的期望(ans[i]实质是前缀和性质) 
    //tmp[k-1]*p[i]:由子树中得最大加k-1,且当前也加
    //tmp[k]*(1-p[i]): 由子树中得最大加k,且当前不加
  } 
  ANS=ans[m][0]*mx[m];
  for (i=1;i<=m;i++)
    ANS+=(ans[m][i]-ans[m][i-1])*(mx[m]+i);
  printf("%.10lf",ANS);
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值