CF494C Helping People
\(CF494C\ \ Helping People\)
题目描述
给一个序列 \(a_1,a_2,a_3,\dots a_n\) 和 \(m\) 次操作,每次可将区间 \([a_i,b_i]\) 加上 \(1\) ,操作成功的概率是 \(p_i\) ,求最后序列中最大值的期望。
对于两个区间 \([a,b],[c,d]\) ,保证 以下条件之一成立:
- 这两个段完全不相交。更正式地说,要么 \(a ≤ b < c ≤ d\) ,要么 \(c ≤ d < a ≤ b\)。
- 两个段中的一个包含在另一个内。更正式地说,要么 \(a ≤ c ≤ d ≤ b\) ,要么 \(c ≤ a ≤ b ≤ d\)。
思路分析
因为任意两个区间,要么完全不相交,要么相包含,所以我们用一个 \(nb\) 的东西——树!
考虑跑树形 \(DP\) ,设 \(dp_{x,i}\) 表示节点 \(x\) 区间中 \(\leq i\) 的概率,类似于前缀和。但是 \(a_i \leq 10^9\),第二维开不下。但是我们发现,\(m\) 次操作最多增加 \(m\) ,所以我们将第二维开到 \(m\) ,类似于存一个变化量。
对于转移节点 \(x\):
- \(x\) 有 \(p_x\) 的概率 \(+1\) ,那么对应的儿子 \(y\) 就应为 \(i+x_{maxn}-y_{maxn}-1\) ,其中 \(maxn\) 是区间最大值,领悟一下就是从 \(y_{maxn}\) 变成 \(x_{maxn}\)
\[dp_{x,i}+=p_x\times \prod_{y\in son_x}dp_{y,i+x_{maxn}-y_{maxn}-1}
\]
- \(x\)又有 \(1-p_x\) 的概率不变,同理:
\[dp_{x,i}+=(1-p_x)\times \prod_{y\in son_x}dp_{y,i+x_{maxn}-y_{maxn}}
\]
- 所以最终的转移方程:
\[dp_{x,i}=p_x\times \prod_{y\in son_x}dp_{y,i+x_{maxn}-y_{maxn}-1}+(1-p_x)\times \prod_{y\in son_x}dp_{y,i+x_{maxn}-y_{maxn}}
\]
另外对于求区间最大值,可以用 \(st\) 表查询 (我是废物,st表打假了调半天)
\(AC\ \ code\)
#include<bits/stdc++.h> using namespace std; #define read read() #define pt puts("") inline int read { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } #define N 100010 #define M 5010 int n,m; int a[N]; int son[M][M]; int sonsum[M]; struct SPX{ int l,r; double p; int maxn; }spx[M]; bool cmp(SPX A,SPX B){ if(A.l==B.l) return A.r>B.r; return A.l<B.l; } int st[N][20]; int Max(int l,int r){ int k=log2(r-l+1); return max(st[l][k],st[r-(1<<k)+1][k]); } int Maxn; double dp[M][M]; double ans; void dfs(int x) { int y; for(int i=1;i<=sonsum[x];i++){ y=son[x][i]; dfs(y); } dp[x][0]=1-spx[x].p; for(int i=1;i<=sonsum[x];i++){ y=son[x][i]; dp[x][0]*=dp[y][spx[x].maxn-spx[y].maxn]; } for(int i=1;i<=m;i++){ double p1=spx[x].p; double p2=1-p1; for(int j=1;j<=sonsum[x];j++){ y=son[x][j]; p1*=dp[y][min(i+spx[x].maxn-spx[y].maxn-1,m)]; p2*=dp[y][min(i+spx[x].maxn-spx[y].maxn,m)]; } dp[x][i]=p1+p2; } } signed main() { n=read;m=read; for(int i=1;i<=n;i++){ a[i]=read;st[i][0]=a[i]; Maxn=max(Maxn,a[i]); } for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); spx[1]=SPX{1,n,0.0,Maxn};m++; for(int i=2;i<=m;i++) { spx[i].l=read; spx[i].r=read; scanf("%lf",&spx[i].p); spx[i].maxn=Max(spx[i].l,spx[i].r); } sort(spx+1,spx+m+1,cmp); for(int i=2;i<=m;i++) for(int j=i-1;j>=1;--j) if(spx[j].l<=spx[i].l && spx[i].r<=spx[j].r){ son[j][++sonsum[j]]=i; break; } dfs(1); ans=dp[1][0]*Maxn; for(int i=1;i<=m;i++) ans+=(dp[1][i]-dp[1][i-1])*(i+Maxn); printf("%.9lf",ans); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现