zoukankan      html  css  js  c++  java
  • 【2019北京集训测试赛(十三)】数据(sj) 冷静分析

    题目大意:给你一个代表区间$[1,n]$的线段树,问你随机访问区间$[1,n]$中的一个子区间,覆盖到的线段树节点个数的期望(需要乘上$frac{n(n-1)}{2}$后输出)。

    数据范围:$n≤10^{18}$

    貌似各位的做法都非常优秀,代码也非常短,那么我来讲一个垃圾做法:

    我们设$f[i]$表示一个构建出$[1,i]$的线段树,随机访问一个子区间覆盖线段树节点个数的期望(为方便处理,乘上了$frac{i(i-1)}{2}$)。

    显然$f[n]$就是答案。

    我们再设$fl[j][i]$表示一棵$[1,i]$的线段树,从左边往右,覆盖了$j$个线段树节点的方案数。

    同理我们处理出$fr[j][i]$

    我们发现:当$j>1$时,选择覆盖$j$个点,这$j$个点显然不会包含整个区间。

    我们设$L=lceil frac{i}{2} ceil$ ,$R=lfloor frac{i}{2} floor $

    那么有$fl[i][j]=fl[j][L]+fl[j-1][r]-[j≤2],fr[i][j]$同理。

    我们考虑$f[i]$要怎么求。

    不难发现,$f[i]$有四种构成方式:只选择了左/右端的区间,两边都选了,恰好选择了根节点。

    只选择一侧的显然是$f[L]+f[R]$,恰好选择根节点的贡献显然为$1$。

    对于两边都选的情况,我们通过枚举$fr[][L]$和$fl[][R]$,简单地乘起来,再乘上总共选择的节点个数,就可以了。

    综上,则有:

    $f[i]=1+f[L]+f[R]+sumlimits_{v_1=1}^{dep_1}sumlimits_{v_2=1}^{dep_2} (v_1+v_2) imesigg(fr[v_1][L] imes fl[v_2][R]-[v1=1,v2=1]igg)$

    其中,$dep1$,$dep2$表示左右两颗子树的最大深度。

    在求解过程中,我们暴力往下递归,我们需要特判$i=1,2,3$的情况,然后就可以了。

    这个复杂度比较垃圾,应该是$O(log^3 n)$的。

    场上真刺激,最后十分钟调处来了23333

     1 #include<bits/stdc++.h>
     2 #define M 998244353
     3 #define L long long
     4 #define MOD 998244353
     5 using namespace std;
     6  
     7 map<L,L> f,fl[100],fr[100],vis,up;
     8  
     9 void solve(L i){
    10     if(vis[i]) return;
    11     if(i==1){
    12         f[i]=fl[1][i]=fr[1][i]=vis[i]=1;
    13         fl[0][i]=fr[0][i]=up[i]=1;
    14         return;
    15     }
    16     if(i==2){
    17         f[i]=3;
    18         fl[1][i]=fr[1][i]=2;
    19         fl[0][i]=fr[0][i]=1;
    20         vis[i]=up[i]=1;
    21         return;
    22     }
    23     if(i==3){
    24         f[i]=7;
    25         fl[1][i]=3; fr[1][i]=2;
    26         fl[0][i]=fr[0][i]=1;
    27         vis[i]=1; up[i]=2;
    28         fr[2][i]=1;
    29         return;
    30     }
    31     L l=(i+1)>>1,r=i-l;
    32     solve(l); solve(r);
    33     int upl=up[l],upr=up[r],UP=max(upl,upr)+1; up[i]=UP;
    34     vis[i]=1;
    35     L sum=f[l]+f[r];
    36     for(int v1=1;v1<=upl;v1++)
    37     for(int v2=1;v2<=upr;v2++){
    38         sum=(sum+1LL*(v1+v2)*(fr[v1][l]*fl[v2][r]%MOD+MOD-(v1==1&&v2==1)))%MOD;
    39     }
    40 //  for(int v1=0;v1<=upl;v1++) sum=(sum-fr[v1][l])%MOD;
    41 //  for(int v2=0;v2<=upr;v2++) sum=(sum-fl[v2][r])%MOD;
    42     f[i]=(sum+1)%MOD;
    43     for(int j=1;j<=UP;j++){
    44         fl[j][i]=(fl[j][l]+fl[j-1][r]-(j<=2)+MOD)%MOD;
    45         int x=fl[j][i];
    46         fr[j][i]=(fr[j-1][l]+fr[j][r]-(j<=2)+MOD)%MOD;
    47         int y=fr[j][i];
    48         x++;
    49     }
    50 //  cout<<fr[2][3]<<endl;
    51     fl[0][i]=fr[0][i]=1;
    52     fl[1][i]++; fr[1][i]++;
    53 }
    54  
    55 int main(){
    56     L n;cin>>n;
    57     solve(n);
    58     cout<<f[n]<<endl;
    59 }
    
    
  • 相关阅读:
    JDBC 复习4 批量执行SQL
    JDBC 复习3 存取Oracle大数据 clob blob
    Oracle复习
    Linux命令(1)grep
    JDBC 复习2 存取mysql 大数据
    JDBC 复习1 DBUtil
    php 环境搭建问题
    Windows 批处理 bat 开启 WiFi 菜单选项 设置ID PWD
    Bat 批处理启动和停止Oracle 服务
    docker 学习1 WSL docker ,Windows docker
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10740203.html
Copyright © 2011-2022 走看看