zoukankan      html  css  js  c++  java
  • 【题解】CodeM美团点评编程大赛复赛 做题感悟&题解

    【T1】

    【简要题意】

      长度为N的括号序列,随机确定括号的方向;对于一个已确定的序列,每次消除相邻的左右括号(右左不行),消除后可以进一步合并和消除直到不能消为止。求剩下的括号的期望。(N leq 2000),保留三位小数。

    【做题过程】

      看上去一道傻逼题,又让我涨姿势了。
      直接按长度DP咯?仔细一想,要记录一直延续至今的待消左括号个数,所以起码是二维。所以就是 (F_{i,j}) 表示处理了前i个点,目前有j个待消的左括号。写了一半我才发现一个问题, $F_{i,j} $ 本身意义不明:到底是到这一个状态的概率(<1),还是到这个状态的期望消除数量?讲道理,单记一个肯定没法转移,而“经验”告诉我以前好像没有一起转移的。所以就GG咯?
      然后强行加一维变成 (F_{i,j,k}) ,k表示目前消除的对数,这样就直接按概率转移即可。现在是(O(N^3))“经验”又告诉我,这种肯定是瞎逼卡精度题。j维可以小一些(很长待命左括号不现实),k维也可以小一些_但是搞着搞着,发现本身F的精度就不够。跑 (N=500) 的时候F每一个值都是0了。
      那还是找规律吧。打表发现,答案可以表示成 (frac {P_i}{Q_i})的形式,其中 (P_i) 是这个数列 (Q_i)(2^ {i-1})。根据OIES的 (P_i)的5阶线性递推(我把(Q_i)也整理到递推里)写了三行代码。。
      woc。。为啥 (N=2000) 又爆精度了?仔细一看,N稍微大一些,每一个答案 (A_i) 就会有误差,然后误差越来越大……理论上可以直接对 (P_i) 进行高精运算(因为是整数没有精度丢失),然后高精除2的幂次算答案,懒得找板子了。(所以理论上此题可以加强,换成求精确值233)

    【题解】

      直接按我最初的想法做即可,正常转移概率和期望数组。

    【UPD】

      一位大佬有一个更简洁霸气的 (O(N)) 做法。我们考虑之前的(2*P_i) 构成的数列,它是A1=2,6,16,38,88,196,432......。
      考虑后面一位减去前面一位的两倍,得到序列A2=2,4,6,12,20,40......
      注意到这个序列实际上是和A3=A000984密切相关的,具体地,A2=A3[1],A3[1]2,A3[2],A3[2]2,A3[3],A3[3]*2......。
      把组合数和要除的2的幂次整理在递推式里,就能(O(N))递推了,实测无精度问题>_<。





    【T2】

    【简要题意】

      给定一棵带点权的树,Q个询问。每次给出向根路径上的两个点(x,y)和一个数值(v),考虑从 (x)(y) 爬,每到一个点时若其点权大于 (v) 就更新 (v) 。每次询问更新的次数。(N,Qleq100000),可以离线。

    【做题过程】

      没仔细想离线,就强上了。若询问不同的 (x) 只有一个,可以维护从根到 (x) 一个单调递减的序列,每次二分 (y) 的位置,看看序列里的点个数即可。 (x) 不同的话,因为树上不能均摊,所以可以对每一个点维护一个可持久化的数组。每次继承父亲的数组,然后二分找到替换的位置并将后面的数字删除。具体实现的时候,可以用可持久化线段树来维护这个操作。不得不说,细节有点萎,而且卡内存常数。

    【题解】

      又是一个精妙的“傻逼”题!首先为了方便可以在询问的x点下面挂一个点,权值就是 (v) ,这样全在树上的节点上进行操作。离线一下,可以构出一棵类似于虚树的树,只是每一个点的父亲为原树上第一个权值比它大的祖先。然后倍增跳一跳即可,NOIP即视感。(所以又可以加强233)



    【T3】

    【简要题意】

      给出一个N个点的基环树,在给定一个质数模域。每一个点有一个待确定的值,对于每一条边((x,y)),给出一个限制(A_i*x+B_i*y=C_i( mod P)) 求每一个点的值,保证解唯一。

    【做题过程&题解】

      傻逼题。找到环然后走一圈解一个方程,再往树枝外面拓展即可。



    【T4】

    【简要题意】

      二维坐标里有N个点 (A_i) (保证所有的横坐标集合和纵坐标集合都是一个1~N的排列)。随机生成一个排列Pi,并执行以下过程:

        now=A[p[1]];
        for (i=2;i<=n;i++)
            if (A[p[i]].x>=now.x&&A[p[i]].y>=now.y) now=A[p[i]];
        return now;
    

      分别对于每一个点,求出最终now是它的排列的个数。(Nleq100000)

    【题解】

      原题题解说的有点迷。
      考虑一个特定序列(B_i)(记录了某排列让now发生变化的所有点的序号),现在计算这个(B_i)对应了多少的排列:首先,显然(B_1)必须放在排列的第一位,现在总排列数有((n-1)!);然后对于一对(B_i)(B_{i+1}),它们在排列的位置之间不能插入任何横纵坐标都大于(B_i)的点;再转化一下,所有横纵坐标都大于(B_i)的点必须出现在(B_{i+1})之后。
      具体地说,假设比(B_1)横纵坐标都大的点有(T_1)个,那么这时的限制就是对于一个确定的(T_1)个点的集合,某一个点(即(B_2))必须出现在集合的最前面。因为在所有排列中,(T_1)个点谁在最前面几率均等,所以$frac {(n-1)!}{T_1} $ 就是目前符合条件数。对于所有的 (B_i)(B_{i+1}) 我们都要满足这样的限制。而且很关键的一点:每次我们强制把(B_{i+1})放在比(B_i)大的点的最前面后,剩下的点在什么位置还是都有可能的,不会对进一步的计算造成一定的限制。所以,对于这个序列(B_i),满足条件的P的数量就是 (frac {(n-1)!}{prod_{i=1}^{|B|-1} T_i})
      现在就是怎么求答案了。对于一个点i,若它是now,那么它就是B序列中的最后一个点。我们枚举它之前的那个B是什么(即枚举横纵坐标都小于它的点)并对所有方案数求和。因此我们知道,对于每一个点我们要维护一个(F_i),它是由比它小的点求和转移过来的,而且最后它还要除掉比它大的点的个数。对于结束点i,不必再除了,最后再乘上((n-1)!)即可。以上过程可简单地用数据结构维护。

    【T5】

    【简要题意】待填。

    【做题过程&题解】

    【T6】

    【简要题意】待填。

    【做题过程&题解】

  • 相关阅读:
    JavaScript 字符串常用操作
    Redis分布式锁
    CSS布局之-水平垂直居中
    vuejs学习笔记(1)--属性,事件绑定,ajax
    Angular.js学习笔记 (一)
    几年前写的一个支持多数据库切换的设计
    bundle中vim相关快捷键的使用
    使用Bundle进行VIM插件的管理
    javascript代码在线测试
    线程池的创建
  • 原文地址:https://www.cnblogs.com/jiangshibiao/p/7140606.html
Copyright © 2011-2022 走看看