2020牛客暑期多校训练营(第九场)—— Groundhog and Gaming Time

2020牛客暑期多校训练营(第九场)—— Groundhog and Gaming Time

输入

6
2 2
1 2
1 4
1 5
3 5
3 6

输出

405536771

备注

1⩽n⩽5×10^5,0⩽Li​⩽Ri​⩽10^9.

题目大意

题解

不同于官方题解通过树形dp的思路,我们的方法是通过累加每种情况对期望的贡献来求解。
线段的交取决于最大的左端点以及最小的右端点,同时维护两个东西比较困难。

当然,我们先添加上最左最优的端点,处理所有线段都不选择的情况;
再按照线段左端点从大到小排序,那么排序后的线段的交,就取决于最小的右端点,以及第一个被选择的线段的左端点。离散化后构建好一棵线段树,映射到原值。ans=∑方案权值∗方案数

AC Code

#include<bits/stdc++.h>
#define ll long long
#define rep(i,x,y) for(ll i=(x);i<=(y);++i)
#define fr first
#define sc second
using namespace std;
const int N=1e6+10;
const int mx=1e9+1;
const int mo=998244353;
const int n2=(mo+1)/2;//2的逆元 
ll a[N*4],b[N*4],p[N];int l[N],r[N],tl[N],tr[N];
void bd(int x,int l,int r)
{
	a[x]=p[r+1]-p[l];
	b[x]=1;
	if(l==r)return;
	int m=(l+r)>>1;
	bd(x*2,l,m);
	bd(x*2+1,m+1,r);
}//构建线段树 
bool cmpl(int x,int y){return l[x]<l[y];}
bool cmpr(int x,int y){return r[x]<r[y];}
void f(int x,int l,int r,ll y,int L,int R)
{
	if(l<=L&&r>=R)
	{
		a[x]=a[x]*y%mo;
		b[x]=b[x]*y%mo;
		return;
	}
	int m=(L+R)>>1;
	if(l<=m)f(x*2,l,r,y,L,m);
	if(r>m)f(x*2+1,l,r,y,m+1,R);
	a[x]=(a[x*2]+a[x*2+1])*b[x]%mo;
}//将区间[l,r]的权值都*y 
int main()
{
	int n;
	scanf("%d",&n);
	int m=2;p[1]=0;p[2]=mx;//添加最左最优的端点
	rep(i,1,n)
	{
		scanf("%d%d",&l[i],&r[i]);
		p[++m]=l[i];
		p[++m]=++r[i];
	}
	sort(p+1,p+m+1);
	m=unique(p+1,p+m+1)-p-1;//离散化
	bd(1,1,m-1);//构建线段树
	rep(i,1,n)
	{
		l[i]=lower_bound(p+1,p+m+1,l[i])-p;
		r[i]=lower_bound(p+1,p+m+1,r[i])-p;
	}
	rep(i,1,n)
	{
		tl[i]=i;
		tr[i]=i;
	}
	sort(tl+1,tl+n+1,cmpl);
	sort(tr+1,tr+n+1,cmpr);//线段按排左右端点排序 
	int t1=1,t2=1;
	ll ans=0;
	rep(i,1,m-1)
	{//遍历区间[i,i+1]
		for(;t1<=n&&l[tl[t1]]<=i;++t1)f(1,l[tl[t1]],r[tl[t1]]-1,2,1,m-1);//添加左端点小于等于i的线段的贡献 
		for(;t2<=n&&r[tr[t2]]<=i;++t2)f(1,l[tr[t2]],r[tr[t2]]-1,n2,1,m-1);//删除右端点小于等于i的线段的贡献 
		ans=(ans+a[1]*(p[i+1]-p[i]))%mo;
	}
	ans=(ans+mo-1ll*mx*mx%mo)%mo;//减去所有线段都不选择的情况 
	rep(i,1,n)ans=ans*n2%mo;//除2^n,得到期望 
	printf("%lld\n",ans);
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值