Codeforces #282 div 1 C Helping People 题解

CF 282 C
Helping People 题解

原题】不贴了。

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

来源简述】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);
}
时间: 2024-10-12 17:33:34

Codeforces #282 div 1 C Helping People 题解的相关文章

【codeforces #282(div 1)】AB题解

A. Treasure time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Malek has recently found a treasure map. While he was looking for a treasure he found a locked door. There was a string s writte

Codeforces Round #282 (Div. 2) a

/**  * @brief Codeforces Round #282 (Div. 2) a  * @file a.cpp  * @author mianma  * @created 2014/12/15 9:55  * @edited  2014/12/15 9:55  * @type math  * @note  */ #include <fstream> #include <iostream> #include <cstring> using namespace 

Codeforces Round #282 (Div. 2) c

/**  * @brief Codeforces Round #282 (Div. 2) c  * @file c.cpp  * @author mianma  * @created 2014/12/16 16:09  * @edited  2014/12/16 16:09  * @type math  * @note  */ #include <fstream> #include <iostream> #include <cstring> #include <c

数学 Codeforces Round #282 (Div. 2) B. Modular Equations

题目传送门 题意:a % x == b,求符合条件的x有几个 数学:等式转换为:a == nx + b,那么设k = nx = a - b,易得k的约数(>b)的都符合条件,比如a=25 b=1,那么24,12, 8, 6, 4, 3, 2都可以,所以只要求出k的约数有几个就可以了,a <= b的情况要特判 /************************************************* Author        :Running_Time* Created Time  

Codeforces Round #282 (Div. 2) b

/**  * @brief Codeforces Round #282 (Div. 2) b  * @file b.cpp  * @author mianma  * @created 2014/12/15 9:55  * @edited  2014/12/15 9:55  * @type math  * @note  */ #include <fstream> #include <iostream> #include <cstring> #include <cma

Codeforces Round #533 (Div. 2)E. Helping Hiasat_求最大独立子集转换求最大团

题目链接:E. Helping Hiasat 题解:把夹在同一段1里面的人互相连边,然后问题就转换为求最大独立子集的大小,最大独立子集大小等于补图的最大团.最大图不会的可以看这篇博客,我也是看这个的 #include<bits/stdc++.h> #include<iostream> #include<string> #include<cstring> #include<algorithm> #define pb push_back #defin

Codeforces #258 Div.2 E Devu and Flowers

大致题意: 从n个盒子里面取出s多花,每个盒子里面的花都相同,并且每个盒子里面花的多数为f[i],求取法总数. 解题思路: 我们知道如果n个盒子里面花的数量无限,那么取法总数为:C(s+n-1, n-1) = C(s+n-1, s). 可以将问题抽象成:x1+x2+...+xn = s, 其中0<=xi <= f[i],求满足条件的解的个数. 两种方法可以解决这个问题: 方法一:这个问题的解可以等价于:mul = (1+x+x^2+...+x^f[1])*(1+x+x^2+...+x^f[2]

Codeforces #259 Div.2

A. Little Pony and Crystal Mine 模拟题. 用矩阵直接构造或者直接根据关系输出 B. Little Pony and Sort by Shift 模拟题. 通过提供的操作得到的序列只能是两段递增或者整个序列递增. 那么可以求得第一段递增序列长度为0-p 如果整个序列是递增,即 p= n-1 那么操作次数就是0. 否则,假设是两段递增,把原始的序列恢复出来,设当前序列是AB,那么A就是0-p BA = (A'B')', '表示对序列进行翻转, 如果BA是有序的,那么需

Codeforces #250 (Div. 2) C.The Child and Toy

之前一直想着建图...遍历 可是推例子都不正确 后来看数据好像看出了点规律 就抱着试一试的心态水了一下 就....过了..... 后来想想我的思路还是对的 先抽象当前仅仅有两个点相连 想要拆分耗费最小,肯定拆相应权值较小的 在这个基础上考虑问题就能够了 代码例如以下: #include <cstdio> #include <iostream> #include <algorithm> #define MAXN 10010 #define ll long long usi