点击打开链接
题意:给定n个树,和m个人,接下来n行是每个树的位置,高度,向左倒的概率,向右倒的概率,接下来给出m个人的位置,判断每个人活下来的概率乘上Zi然后相加。
思路:涌用线段树的节点维护概率,先进行离散化,本来的话可能应该用到成段更新,但这题可以避免,下面会给出注释。
#include <map> #include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=100010; double num[2000010];//离散化后的点可能会变多了2倍 vector<int> G; map<int,int> pp; void buildtree(int le,int ri,int node){ num[node]=1; if(le==ri) return ; int t=(le+ri)>>1; buildtree(le,t,node<<1); buildtree(t+1,ri,node<<1|1); } void update(int l,int r,double add,int le,int ri,int node){ if(l<=le&&ri<=r){ num[node]*=add; return ; } int t=(le+ri)>>1; if(l<=t) update(l,r,add,le,t,node<<1); if(r>t) update(l,r,add,t+1,ri,node<<1|1); } double query(int pos,int le,int ri,int node){ if(le==ri) return num[node]; int t=(le+ri)>>1; if(pos<=t) return num[node]*query(pos,le,t,node<<1);//因为这里返回的是概率相乘,相当于已经更新,和懒惰标记思想差不多 else return num[node]*query(pos,t+1,ri,node<<1|1); } int main(){ int L[maxn],R[maxn],X[maxn],H[maxn],P[10010],n,m,B[10010]; while(scanf("%d%d",&n,&m)!=-1){ G.clear(); for(int i=0;i<n;i++){ scanf("%d%d%d%d",&X[i],&H[i],&L[i],&R[i]); G.push_back(X[i]-H[i]); G.push_back(X[i]-1);//题目上是开区间,所以是杀不死站在X[i]的人的,因此减去1; G.push_back(X[i]+H[i]); G.push_back(X[i]+1);//这里一样,因此加1; } for(int i=0;i<m;i++){ scanf("%d%d",&B[i],&P[i]); G.push_back(B[i]); } sort(G.begin(),G.end()); G.resize(unique(G.begin(),G.end())-G.begin());//进行离散化 int len=G.size(); buildtree(1,len,1); for(int i=0;i<len;i++){ pp[G[i]]=i+1; } for(int i=0;i<n;i++){ update(pp[X[i]-H[i]],pp[X[i]-1],1.0-double((double)L[i]/100),1,len,1); update(pp[X[i]+1],pp[X[i]+H[i]],1.0-double((double)R[i]/100),1,len,1); } double ans=0; for(int i=0;i<m;i++){ ans+=query(pp[B[i]],1,len,1)*P[i]; } printf("%.10f\n",ans); } return 0; }