zoukankan      html  css  js  c++  java
  • 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
    ",ans%p);
    133     return 0;
    134 }
    View Code
  • 相关阅读:
    C++ 获取ms级的计时
    基于UDP的IP对IP的客户端程序
    stm32 keil生成bin文件
    xmos 加密
    DMX512程序介绍
    WS2812原理及实现
    MFC 通过按钮调用自对话框 给按钮加载位图 给对话框添加背景
    4*4矩阵键盘FPGA扫描实现
    FIFO
    Modelsim建立UVM环境
  • 原文地址:https://www.cnblogs.com/liutianrui/p/13598854.html
Copyright © 2011-2022 走看看