<span>模拟104 题解</span>
A. 中间值
二分答案在数组$a$中的排名,则得到期望的在数组$b$中的排名。
当数组$b$中对应的数恰好是大于二分值的第一个数,代表二分到了答案。
通过对应的数与当前数的大小关系,可以确定二分方向。
同理还应当二分答案在数组$b$中的排名。
然而这个做法实现比较困难,还有另一个更帅的二分做法。
考虑当前的问题:
数组$a$,单调区间$[l_1,r_1]$,数组$b$,单调区间$[l_2,r_2]$,查询共同区间的第$k$大值。
不妨将$k$折半,分别对应在$a$,$b$数组中,
可以得到$a_{r_1-\frac{k}{2}}$,$b_{r_2-\frac{k}{2}}$。
将二者比较可以确定一段无用的区间。
设$a_{r_1-\frac{k}{2}}<b_{r_2-\frac{k}{2}}$,$b$数组在大于$r_2-\frac{k}{2}$的部分只会更大。
所以问题的规模降低了一半,变为了问题
数组$a$,单调区间$[l_1,r_1]$,数组$b$,单调区间$[l_2,r_2-\frac{k}{2}]$,查询共同区间的第$\frac{k}{2}$大值。
所以可以$O(logn)$处理每组询问。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int N=5e5+7; 5 const int inf=0x3f3f3f3f; 6 int n,m; 7 int a[N],b[N]; 8 inline int query(int l1,int r1,int l2,int r2){ 9 int l,r,lst,nxt,ans=-1,len1=r1-l1+1,len2=r2-l2+1,aim=len1+len2+1>>1; 10 lst=b[l2-1]; nxt=b[r2+1]; 11 b[l2-1]=-1; b[r2+1]=inf; 12 l=l1; r=r1; 13 while(l<=r){ 14 int mid=l+r>>1,rk=mid-l1+1,to=aim-rk+l2;//大于 的第一个 15 if(to<l2){ 16 r=mid-1; 17 continue; 18 } 19 if(to>r2+1){ 20 l=mid+1; 21 continue; 22 } 23 if(b[to]>=a[mid]&&b[to-1]<=a[mid]){ 24 ans=a[mid]; 25 break; 26 } 27 if(l==r) break; 28 if(b[to-1]>a[mid]) l=mid+1; 29 else r=mid-1; 30 } 31 b[l2-1]=lst; b[r2+1]=nxt; 32 if(~ans) return ans; 33 lst=a[l1-1]; nxt=a[r1+1]; 34 a[l1-1]=-1; a[r1+1]=inf; 35 l=l2; r=r2; 36 while(l<=r){ 37 int mid=l+r>>1,rk=mid-l2+1,to=aim-rk+l1; 38 if(to<l1){ 39 r=mid-1; 40 continue; 41 } 42 if(to>r1+1){ 43 l=mid+1; 44 continue; 45 } 46 if(a[to]>=b[mid]&&a[to-1]<=b[mid]){ 47 ans=b[mid]; 48 break; 49 } 50 if(a[to-1]>b[mid]) l=mid+1; 51 else r=mid-1; 52 } 53 a[l1-1]=lst; a[r1+1]=nxt; 54 if(~ans) return ans; 55 return 0; 56 } 57 inline int read(register int x=0,register char ch=getchar(),register char f=0){ 58 for(;!isdigit(ch);ch=getchar()) f=ch=='-'; 59 for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); 60 return f?-x:x; 61 } 62 int main(){ 63 freopen("median.in","r",stdin); 64 freopen("median.out","w",stdout); 65 n=read(); m=read(); a[0]=b[0]=-1; a[n+1]=b[n+1]=inf; 66 for(int i=1;i<=n;++i) a[i]=read(); 67 for(int i=1;i<=n;++i) b[i]=read(); 68 for(int i=1,opt,l1,r1,l2,r2;i<=m;++i){ 69 opt=read(); 70 if(opt==1){ 71 l1=read(); r1=read(); l2=read(); 72 if(l1==0) a[r1]=l2; 73 else b[r1]=l2; 74 } 75 else{ 76 l1=read(); r1=read(); l2=read(); r2=read(); 77 printf("%d\n",query(l1,r1,l2,r2)); 78 } 79 } 80 return 0; 81 }
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int N=5e5+7; 5 const int inf=0x3f3f3f3f; 6 int n,m; 7 int a[N],b[N]; 8 int solve(int st1,int ed1,int st2,int ed2,int k){ 9 if(k==1){ 10 if(st1<=ed1&&st2<=ed2) return max(a[ed1],b[ed2]); 11 return st1<=ed1?a[ed1]:b[ed2]; 12 } 13 int x=k>>1,y=k-x,to1=ed1-x,to2=ed2-y; 14 if(to1<st1-1) return solve(st1,ed1,st2,to2,k-y); 15 if(to2<st2-1) return solve(st1,to1,st2,ed2,k-x); 16 if(a[to1+1]>=b[to2+1]) return solve(st1,to1,st2,ed2,k-x); 17 return solve(st1,ed1,st2,to2,k-y); 18 } 19 inline int read(register int x=0,register char ch=getchar(),register char f=0){ 20 for(;!isdigit(ch);ch=getchar()) f=ch=='-'; 21 for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); 22 return f?-x:x; 23 } 24 int main(){ 25 freopen("median.in","r",stdin); 26 freopen("median.out","w",stdout); 27 n=read(); m=read(); 28 for(int i=1;i<=n;++i) a[i]=read(); 29 for(int i=1;i<=n;++i) b[i]=read(); 30 for(int i=1,opt,l1,r1,l2,r2;i<=m;++i){ 31 opt=read(); 32 if(opt==1){ 33 l1=read(); r1=read(); l2=read(); 34 if(l1==0) a[r1]=l2; 35 else b[r1]=l2; 36 } 37 else{ 38 l1=read(); r1=read(); l2=read(); r2=read(); 39 printf("%d\n",solve(l1,r1,l2,r2,r2+r1-l1-l2+3>>1)); 40 } 41 } 42 return 0; 43 }
B. 最小值
首先写出弱智$dp$。
$dp_i$表示以$i$为结尾的几段区间的最优决策下的答案。
$dp_i=dp_{j-1}+f(min_{k=j}^i a_k)$。
这个东西只与两点间的最值有关,所以单调栈维护最值的决策区间,
顺便线段树维护一下$dp$的最优决策就完了。
C. 最大值
首先是一个整数期望公式。
$x$为一个随机变量,那么
$E(x)=\sum \limits_{i=1}^{\inf}P(x==i)*i$
$E(x)=\sum \limits_{i=1}^{\inf}P(x==i)\sum \limits_{j=1}^{i}$
改变枚举顺序,有
$E(x)=\sum \limits_{i=1}^{\inf}P(x>=i)$
应当注意到,本题中每组询问是独立互不影响的,
也就是说可以分别统计每组询问的答案,之后简单求和。
求单组询问的期望,枚举$i$,问题是求$P(max_{j=l}^{r}v_j)>=i)$
$=1-\Pi_{j=l}^{r}P(v_j<x)$
$=1-\Pi_{j=l}^{r}1-P(v_j>=x)$
对于一个点,$P(v_j>=x)$是容易求出的。
题中的特殊性质是,不存在区间包含的情况,
所以对所有区间按左端点排序,包含一个点的区间是连续的。
对于相邻两个$x$,可以将答案简单的除原概率,乘上新的概率。
注意到本题中的$P(v_j>=x)$是单调的,只要倒序枚举$x$,就可以有效避免除$0$的情况。
对所有的询问建树,问题转化为区间乘法(当然还有通过乘逆元进行的除法),顺便维护整体的和就可以了。
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #define lch p<<1 7 #define rch p<<1|1 8 #define ll long long 9 using namespace std; 10 const int mod=1e9+7; 11 const int N=2e5+7; 12 int n,m,q,cnt; 13 int lsh[N],L[N],R[N]; 14 vector<pair<int,ll> > ve[N],f[N]; 15 inline int read(register int x=0,register char ch=getchar(),register char f=0){ 16 for(;!isdigit(ch);ch=getchar()) f=ch=='-'; 17 for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); 18 return f?-x:x; 19 } 20 inline ll qpow(ll x,int k,ll r=1){ 21 for(;k;k>>=1,x=x*x%mod) if(k&1) r=r*x%mod; 22 return r; 23 } 24 struct Query{ 25 int l,r; 26 }que[N]; 27 struct node{ 28 int l,r; 29 ll val,lzy; 30 }s[N<<2]; 31 void build(int p,int l,int r){ 32 s[p].l=l; s[p].r=r; s[p].lzy=1; s[p].val=r-l+1; 33 if(l==r) return ; 34 int mid=l+r>>1; 35 build(lch,l,mid); 36 build(rch,mid+1,r); 37 } 38 inline void down(int p){ 39 s[lch].val=s[p].lzy*s[lch].val%mod; 40 s[rch].val=s[p].lzy*s[rch].val%mod; 41 s[lch].lzy=s[p].lzy*s[lch].lzy%mod; 42 s[rch].lzy=s[p].lzy*s[rch].lzy%mod; 43 s[p].lzy=1; 44 } 45 void mult(int p,int l,int r,ll val){ 46 if(s[p].l>=l&&s[p].r<=r) return s[p].val=s[p].val*val%mod,s[p].lzy=s[p].lzy*val%mod,void(); 47 if(s[p].lzy!=1) down(p); 48 if(l<=s[lch].r) mult(lch,l,r,val); 49 if(r>=s[rch].l) mult(rch,l,r,val); 50 s[p].val=(s[lch].val+s[rch].val)%mod; 51 } 52 int main(){ 53 freopen("max.in","r",stdin); 54 freopen("max.out","w",stdout); 55 n=read(); m=read(); q=read(); 56 for(int i=1,x,y,p;i<=m;++i){ 57 x=read(); y=read(); p=read(); lsh[++cnt]=y; 58 ve[x].push_back(make_pair(y,p)); 59 } 60 sort(lsh+1,lsh+cnt+1); cnt=unique(lsh+1,lsh+cnt+1)-lsh-1; 61 for(int i=1;i<=n;++i){ 62 sort(ve[i].begin(),ve[i].end()); 63 for(int j=0;j<ve[i].size();++j) ve[i][j].first=lower_bound(lsh+1,lsh+cnt+1,ve[i][j].first)-lsh; 64 for(int j=0;j<ve[i].size();++j){ 65 if(f[i].empty()||ve[i][j].first!=f[i].back().first) f[i].push_back(ve[i][j]); 66 else f[i].back().second=((1-(1-f[i].back().second)*(1-ve[i][j].second))%mod+mod)%mod; 67 } 68 ve[i].clear(); 69 ll p=1,po=0;//前面一个都没出现的概率 70 for(int j=0;j<f[i].size();++j){ 71 ll tmp=f[i][j].second; 72 f[i][j].second=p*f[i][j].second%mod; 73 p=p*(1-tmp+mod)%mod; 74 po=(po+f[i][j].second)%mod; 75 } 76 f[i].push_back(make_pair(0,1-po+mod)); 77 sort(f[i].begin(),f[i].end()); 78 for(int j=f[i].size()-2;~j;--j) (f[i][j].second+=f[i][j+1].second)%=mod; 79 } 80 for(int i=1;i<=n;++i) for(int j=0;j<f[i].size();++j) ve[f[i][j].first].push_back(make_pair(i,j)); 81 for(int i=1;i<=n;++i) f[i].push_back(make_pair(cnt+1,0)); 82 ll ans=0; 83 for(int i=1;i<=q;++i) que[i].l=read(),que[i].r=read(); 84 sort(que+1,que+q+1,[](const Query &x,const Query &y){return x.l<y.l;}); 85 L[0]=1; R[n+1]=q; 86 for(int i=1;i<=n;++i){ 87 L[i]=L[i-1]; 88 while(L[i]<=q&&que[L[i]].r<i) ++L[i]; 89 } 90 for(int i=n;i;--i){ 91 R[i]=R[i+1]; 92 while(R[i]&&que[R[i]].l>i) --R[i]; 93 } 94 build(1,1,q); 95 for(int i=cnt;i;--i){ 96 for(int j=0;j<ve[i].size();++j){ 97 int pos=ve[i][j].first; 98 if(L[pos]>R[pos]) continue; 99 mult(1,L[pos],R[pos],qpow(1-f[pos][ve[i][j].second+1].second+mod,mod-2)); 100 mult(1,L[pos],R[pos],1-f[pos][ve[i][j].second].second); 101 } 102 (ans+=(lsh[i]-lsh[i-1])*(q-s[1].val)%mod)%=mod; 103 } 104 printf("%lld\n",(ans+mod)%mod); 105 return 0; 106 }