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

题意:

在0~1e9 上一共有n个区间,每个区间有0.5的概率出现,问出现的区间的并集的长度的平方期望为多少。

由于是长度的平方,无法直接将区间长度相加,所以,我们考虑化式子让问题变得简单。

显然:

(a+b+c+d+e)^2=a*(a+b+c+d+e)+b*(a+b+c+d+e)+c*(a+b+c+d+e)+d*(a+b+c+d+e)+e*(a+b+c+d+e)

(a+b+c)^2+(c+d+e)^2=…… c*(a+b+c)+c*(c+d+e) ……

=…… c*((a+b+c)+(c+d+e)) ……

我们可以借助这个式子求出来每一个由n个区间的左右端点划分成的2n-1个小区间对于答案的贡献。

所以对于一个小区间,它的贡献是 (长度)*(所有包含它的大区间并集的长度和)

小区间的长度很好求,难得是求所有包含它的大区间并集的长度和。

我们不妨通过求各个小区间的贡献求出这些区间的并集的长度和。

每个小区间的贡献为(2^cnt-1)*(小区间长度),我们发现-1非常烦人。

但是我们可以通过化式子发现所有的-1项的和恰好为整个区间的长度的平方,可以在之后一次结清。

这样,每个小区间的贡献就为(2^cnt)*(区间长度)

这就非常好求了,我们可以通过线段树来进行维护。


现在让我们整理一下思路:

首先,我们拆分出一个个小区间,然后对于从左到右枚举这些小区间。

当我们枚举到一个小区间的时候,我们用线段树去求出所有包含它的大区间并集的长度和。

之后,我们减去区间长度的平方并除以2^n。得到答案

具体实现细节请看代码。

  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<map>
  8 #define N 500005
  9 using namespace std;
 10 int n;
 11 struct ro{
 12     int l,r;
 13 }A[N];
 14 const int p=998244353;
 15 int inv;
 16 int B[N],C[N];
 17 bool cmpl(int x,int y)
 18 {
 19     return A[x].l<A[y].l;
 20 }
 21 bool cmpr(int x,int y)
 22 {
 23     return A[x].r<A[y].r;
 24 }
 25 map<int,int>ma;
 26 int zz,D[2*N];
 27 struct no{
 28     int left,right,mid;
 29     long long sum,tmp;
 30 }node[N*8];
 31 void build(int left,int right,int x)
 32 {
 33     node[x].left=left,node[x].right=right;
 34     node[x].tmp=1;                        //tmp表示这个区间被覆盖了log2(tmp)次 
 35     node[x].sum=D[right+1]-D[left];        //sum表示这个区间的贡献和 
 36     if(left==right)
 37     {
 38         return;
 39     }
 40     int mid=(left+right)>>1;
 41     node[x].mid=mid;
 42     build(left,mid,x<<1);
 43     build(mid+1,right,x<<1|1);
 44 }
 45 long long ksm(long long x,long long z)
 46 {
 47     long long ans=1;
 48     while(z)
 49     {
 50         if(z&1)
 51         {
 52             ans=ans*x%p;
 53         }
 54         x=x*x%p; 
 55         z>>=1;
 56     }
 57     return ans;
 58 }
 59 void change(int left,int right,int x,int da)
 60 {
 61     if(node[x].left==left&&node[x].right==right)
 62     {
 63         node[x].sum=node[x].sum*da%p;
 64         node[x].tmp=node[x].tmp*da%p;
 65         return ;    
 66     }
 67     int mid=node[x].mid;
 68     if(left>mid) change(left,right,x<<1|1,da);
 69     else if(right<=mid) change(left,right,x<<1,da);
 70     else change(left,mid,x<<1,da),change(mid+1,right,x<<1|1,da);
 71     node[x].sum=(node[x<<1].sum+node[x<<1|1].sum)%p*node[x].tmp%p;
 72 }
 73 int main()
 74 {
 75     inv=ksm(2,p-2);
 76     scanf("%d",&n);
 77     for(int i=1;i<=n;i++)
 78     {
 79         scanf("%d%d",&A[i].l,&A[i].r);
 80         A[i].r++;                    //方便计算让右端点+1 
 81         B[i]=C[i]=i;
 82         if(!ma[A[i].l])
 83         {
 84             zz++;
 85             D[zz]=A[i].l;
 86             ma[A[i].l]=1;
 87         }
 88         if(!ma[A[i].r])
 89         {
 90             zz++;
 91             D[zz]=A[i].r;
 92             ma[A[i].r]=1;
 93         }
 94     }
 95     if(!ma[0])                        //在外面套上一个边界,方便计算 
 96     {
 97         zz++;
 98         D[zz]=0;
 99         ma[0]=zz;
100     }
101     if(!ma[1e9+1])
102     {
103         zz++;
104         D[zz]=1e9+1;
105         ma[1e9+1]=zz;
106     }
107     sort(B+1,B+n+1,cmpl);            //将区间按照左端点排序 
108     sort(C+1,C+n+1,cmpr);            //将区间按照右端点排序 
109     sort(D+1,D+zz+1);
110     for(int i=1;i<=zz;i++)
111     {
112         ma[D[i]]=i;
113     }
114     int li=1,ri=1;
115     build(1,zz-1,1);
116     long long ans=0;
117     for(int i=1;i<zz;i++)
118     {
119         for(;A[B[li]].l<=D[i]&&li<=n;li++)    //计算新的对 [ D[i],D[i+1]) 有影响的大区间 的贡献 
120         {
121             change(ma[A[B[li]].l],ma[A[B[li]].r]-1,1,2);
122         }
123             
124         for(;A[C[ri]].r<=D[i]&&ri<=n;ri++) //消去新的不对[ D[i],D[i+1]) 有影响的大区间 的贡献 
125         {
126             change(ma[A[C[ri]].l],ma[A[C[ri]].r]-1,1,inv);
127         }
128         ans=(ans+1ll*node[1].sum*(D[i+1]-D[i])%p)%p;
129     }
130     ans=(ans-1ll*(1000000001)*(1000000001)%p+p)%p;     // 减去多算的那部分贡献 
131     ans=ans*ksm(inv,n)%p;                           //除以2^n 
132     printf("%lld\n",ans%p);
133     return 0;
134 }
View Code

 

posted @ 2020-09-01 21:54  Hzoi_joker  阅读(254)  评论(0编辑  收藏  举报