牛客多校第九场C-Groundhog and Gaming Time

Description

牛客多校第九场C-Groundhog and Gaming Time_第1张图片

Solution

计算期望有两种常用方法,计算贡献和期望DP,这里我们选择前者,后者可参考官方题解
我们把区间左右端点升序排序,考虑每一段区间的贡献
设区间 Q Q Q出现在 a a a个线段中
P ( Q ) = 2 a − 1 2 n P(Q)=\cfrac{2^a-1}{2^n} P(Q)=2n2a1,减一是因为不能全部不选
∴ E ( Q ) = 2 a − 1 2 n ∗ ∣ Q ∣ \therefore E(Q)=\cfrac{2^a-1}{2^n}*|Q| E(Q)=2n2a1Q
那么在 n n n条线段中, P i P_i Pi的贡献( b i b_i bi条线段包含 Q Q Q
∣ P i ∣ ∗ ∑ E ( Q ) |P_i|*\sum E(Q) PiE(Q)
= ∣ P i ∣ ∗ ∑ j ( 2 b j − 1 2 n ∗ ∣ Q j ∣ ) =|P_i|*\sum_{j}(\cfrac{2^{b_j}-1}{2^n}*|Q_j|) =Pij(2n2bj1Qj)
= ∣ P i ∣ ∗ ∑ j ( 2 b j 2 n ∗ ∣ Q j ∣ ) − ∣ P i ∣ ∗ m x =|P_i|*\sum_{j}(\cfrac{2^{b_j}}{2^n}*|Q_j|)-|P_i|*mx =Pij(2n2bjQj)Pimx
我们用线段树维护 2 b i 2^{b_i} 2bi(区间乘or除),最后除 2 n 2^n 2n并减掉 m x mx mx

#include 
#define ll long long
#define ls x<<1
#define rs x<<1|1
using namespace std;
const int N=1e6+10,P=998244353,inf=2e9;
ll p[N],n,m=2,l[N],r[N],tl[N],tr[N];
struct Seg{int l,r;ll val,mul;}T[N<<2];
void up(int x){T[x].val=(T[ls].val+T[rs].val)*T[x].mul%P;}
void build(int x,int l,int r)
{
	T[x].l=l,T[x].r=r,T[x].val=p[r+1]-p[l],T[x].mul=1;
	if(l==r) return ;
	int m=l+r>>1;
	build(ls,l,m),build(rs,m+1,r),up(x);
}
void modify(int x,int l,int r,ll w)
{
	if(T[x].l==l&&r==T[x].r){
		T[x].val=T[x].val*w%P;
		T[x].mul=T[x].mul*w%P;
		return ;
	}
	int m=T[x].l+T[x].r>>1;
	if(r<=m) modify(ls,l,r,w);
	else if(l>m) modify(rs,l,r,w);
	else modify(ls,l,m,w),modify(rs,m+1,r,w);
	up(x);
}
bool cmpl(int x,int y){return l[x]<l[y];}
bool cmpr(int x,int y){return r[x]<r[y];}
ll ksm(ll a,ll b){
	ll ret=1ll;
	while(b){if(b&1ll)ret=ret*a%P;a=a*a%P,b>>=1ll;}
	return ret;
}int main(){
	scanf("%lld",&n),p[1]=0,p[2]=inf;
	ll inv=ksm(2,P-2);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",l+i,r+i);
		p[++m]=l[i];
		p[++m]=++r[i];
		tl[i]=tr[i]=i;
	}
	sort(p+1,p+m+1);
	m=unique(p+1,p+m+1)-p-1;
	build(1,1,m-1);
	for(int i=1;i<=n;i++){
		l[i]=lower_bound(p+1,p+m+1,l[i])-p;
		r[i]=lower_bound(p+1,p+m+1,r[i])-p;
	}
	sort(tl+1,tl+n+1,cmpl);
	sort(tr+1,tr+n+1,cmpr);
	int t1=1,t2=1;
	ll ans=0;
	for(int i=1;i<m;++i){//遍历区间[i,i+1],维护包含该区间的线段的交集的期望。这个期望通过计算所有区间的贡献和得到 
	//从m个线段,找出a个线段包含区间[i,i+1]。计算这a个线段的交集的期望,等于每个区间的贡献总和。
	//a个线段,有b个线段覆盖区间[j,j+1],则区间[j,j+1]的贡献 = 长度*(2^b-1)/2^n。其中-1和/2^n最后再去除 
		for(;t1<=n&&l[tl[t1]]<=i;++t1) modify(1,l[tl[t1]],r[tl[t1]]-1,2);
//		printf("%d %d %lld\n",t1,t2,ans);
		for(;t2<=n&&r[tr[t2]]<=i;++t2) modify(1,l[tr[t2]],r[tr[t2]]-1,inv);
		ans=(ans+T[1].val*(p[i+1]-p[i])%P)%P;
//		printf("%d %d %lld %lld\n",t1,t2,ans,T[1].val);
	}ans=(ans+P-1ll*inf*inf%P)%P;
	printf("%lld\n",ans*ksm(inv,n)%P);
}

你可能感兴趣的:(2020牛客暑期多校训练营)