2017 Multi-University Training Contest 4 solutions BY 陈松杨

2017 Multi-University Training Contest 4 solutions BY 陈松杨

1001. Big Integer

如果知道k-1k1个数的个数c_1,c_2,...,c_{k-1}c1,c2,...,ck1,那么方案数就是:

\frac{\left(\sum\limits_{i=1}^{k-1} c_i\right)!}{\prod\limits_{i=1}^{k-1}c_i!}i=1k1ci!(i=1k1ci)!

构造多项式f_i(x)=\sum\limits_{j=0}^n \frac{g_{i,j}}{j!}x^jfi(x)=j=0nj!gi,jxj,则将这k-1k1个多项式乘起来之后,每一项乘以i!i!即可得到答案,可以用NTT在O(nk^2\log(nk))O(nk2log(nk))的时间内预处理出初始答案。

注意到我们只关心每个时刻答案的和,故可以在DFT意义下直接累加答案,最后再将结果IDFT回来。

对于单点修改操作,可以看成是给某个多项式叠加上一个只有一项系数不为00的多项式,观察NTT的公式:

y_n=\sum_{i=0}^{d-1} x_i\times\left(g^\frac{P-1}{d}\right)^{in}\bmod Pyn=i=0d1xi×(gdP1)inmodP

那么它在DFT后的结果是一个等比数列,故直接对原始多项式叠加一个等比数列即可完成修改。

对于叠加多项式,直接O(k)O(k)重新计算乘积是不可取的,正确方法是对于每一项记录非00的乘积以及00的个数,单次修改时间复杂度为O(1)O(1)。每次操作的时间复杂度为O(nk)O(nk)

总时间复杂度O(nk^2\log(nk)+mnk)O(nk2log(nk)+mnk)

1002. Classic Quotation

首先通过KMP算法预处理出TTnextnext数组,设:

pref_iprefi表示SS的前缀iiTT进行KMP后KMP的指针到达了哪里。

preg_ipregi表示SS的前缀iiTT出现的次数。

suf_{i,j}sufi,j表示从SS的后缀ii,从jj指针开始KMP,能匹配多少个TT

那么前缀ii拼接上后缀jjTT的个数为preg_i+suf_{j,pref_i}pregi+sufj,prefi

pregpregpregpreg的前缀和,sufsufsufsuf的后缀和,s_{i,j}si,j表示ii前面中prefprefjj的个数,那么对于询问L,RL,R

ans=\sum_{i=1}^L\sum_{j=R}^n preg_i+suf_{j,pref_i}=(n-R+1)preg_L+\sum_{i=0}^{m-1}s_{L,i}\times suf_{R,i}ans=i=1Lj=Rnpregi+sufj,prefi=(nR+1)pregL+i=0m1sL,i×sufR,i

以上所有数组均可以使用KMP和递推求出,时间复杂度O((n+k)m)O((n+k)m)

1003. Counting Divisors

n=p_1^{c_1}p_2^{c_2}...p_m^{c_m}n=p1c1p2c2...pmcm,则d(n^k)=(kc_1+1)(kc_2+1)...(kc_m+1)d(nk)=(kc1+1)(kc2+1)...(kcm+1)

枚举不超过\sqrt{r}r的所有质数pp,再枚举区间[l,r][l,r]中所有pp的倍数,将其分解质因数,最后剩下的部分就是超过\sqrt{r}r的质数,只可能是00个或11个。

时间复杂度O(\sqrt{r}+(r-l+1)\log\log(r-l+1))O(r+(rl+1)loglog(rl+1))

1004. Dirt Ratio

二分答案midmid,检验是否存在一个区间满足\frac{size(l,r)}{r-l+1}\leq midrl+1size(l,r)mid,也就是size(l,r)+mid\times l\leq mid\times (r+1)size(l,r)+mid×lmid×(r+1)

从左往右枚举每个位置作为rr,当rr变化为r+1r+1时,对sizesize的影响是一段区间加11,线段树维护区间最小值即可。

时间复杂度O(n\log n\log w)O(nlognlogw)

1005. Lazy Running

w=\min(d_{1,2},d_{2,3})w=min(d1,2,d2,3),那么对于每一种方案,均可以通过往返跑ww这条边使得距离增加2w2w。也就是说,如果存在距离为kk的方案,那么必然存在距离为k+2wk+2w的方案。

dis_{i,j}disi,j表示从起点出发到达ii,距离模2w2wjj时的最短路,那么根据dis_{2,j}dis2,j解不等式即可得到最优路线。

时间复杂度O(w\log w)O(wlogw)

1006. Logical Chain

对于每个询问,求出强连通分量,那么一个点数为tt的强连通分量对答案的贡献为\frac{t(t-1)}{2}2t(t1)

利用Kosaraju算法,只需要在正反图各做一次DFS即可求出强连通分量。面对稠密图,Kosaraju算法的瓶颈在于寻找与点xx相连且未访问过的点。考虑用bitset来保存边表g_xgx,以及未访问过的点集SS,那么只需要取出g_x\&Sgx&S内的所有11即可在O(\frac{n^2}{64})O(64n2)的时间内求出强连通分量。

时间复杂度O(\frac{mn^2}{64})O(64mn2)

1007. Matching In Multiplication

首先如果一个点的度数为11,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为11的点。

那么剩下的图中左右各有mm个点,每个点度数都不小于22,且左边每个点度数都是22,而右侧总度数是2m2m,因此右侧只能是每个点度数都是22。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。

时间复杂度O(n)O(n)

1008. Phone Call

不难发现这个过程就是Prim算法求最小生成树的过程,用Kruskal算法同样正确。

将所有线路按代价从小到大排序,对于每条线路(a,b,c,d)(a,b,c,d),首先把aabb路径上的点都合并到LCA,再把ccdd路径上的点都合并到LCA,最后再把两个LCA合并即可。

f_ifi表示ii点往上深度最大的一个可能不是和ii在同一个连通块的祖先,每次沿着ff跳即可。用路径压缩的并查集维护这个ff即可得到优秀的复杂度。

时间复杂度O(m\log m)O(mlogm)

1009. Questionnaire

m=2m=2,必然存在一组可行解。

1010. Security Check

f_{i,j}fi,j表示仅考虑a[1..i]a[1..i]b[1..j]b[1..j]时,最少需要多少时间。

|a_i-b_j|>kaibj>k,则f_{i,j}=f_{i-1,j-1}+1fi,j=fi1,j1+1,否则f_{i,j}=\min(f_{i-1,j},f_{i,j-1})+1fi,j=min(fi1,j,fi,j1)+1

注意到后者只有O(nk)O(nk)个,可以暴力DP,前者可以通过二分找到最大的tt,满足i,ji,j往前tt个均不冲突,然后再从某个后者状态转移过来。

时间复杂度O(nk\log n)O(nklogn)

1011. Time To Get Up

按题意模拟即可。

1012. Wavel Sequence

f_{i,j,k}fi,j,k表示仅考虑a[1..i]a[1..i]b[1..j]b[1..j],选择的两个子序列结尾分别是a_iaib_jbj,且上升下降状态是kk 时的方案数,则f_{i,j,k}=\sum f_{x,y,1-k}fi,j,k=fx,y,1k,其中xx<i,y<j。暴力转移的时间复杂度为O(n^4)O(n4),不能接受。

考虑将枚举决策点x,yx,y的过程也DP掉。设g_{i,y,k}gi,y,k表示从某个f_{x,y,k}fx,y,k作为决策点出发,当前要更新的是ii的方案数,h_{i,j,k}hi,j,k表示从某个f_{x,y,k}fx,y,k作为决策点出发,已经经历了gg的枚举,当前要更新的是jj的方案数。转移则是要么开始更新,要么将ii或者jj继续枚举到i+1i+1以及j+1j+1。因为每次只有一个变量在动,因此另一个变量恰好可以表示上一个位置的值,可以很方便地判断是否满足上升和下降。

时间复杂度O(n^2)O(n2)

1013. Yuno And Claris

先考虑如何求区间第kk小值。对序列和权值都进行分块,设b_{i,j}bi,j表示前jj块中权值在ii块内的数字个数,c_{i,j}ci,j表示前jj块中数字ii的出现次数。那么对于一个询问[l,r][l,r],首先将零碎部分的贡献加入到临时数组tbtbtctc中,然后枚举答案位于哪一块,确定位于哪一块之后再暴力枚举答案即可在O(\sqrt{n})O(n)的时间内求出区间第kk小值。

接着考虑如何实现区间[l,r][l,r]xx变成yy的功能。显然对于零碎的两块,可以直接暴力重构整块。对于中间的每个整块,如果某一块不含xx,那么无视这一块;否则如果这一块不含yy,那么只需要将xx映射成yy;否则这一块既有xx又有yy,这意味着xxyy之间发生了合并,不妨直接暴力重构整块。因为有cc数组,我们可以在O(1)O(1)的时间内知道某一块是否有某个数。

考虑什么情况下会发生重构,也就是一个块内发生了一次合并的时候。一开始长度为nn的序列会提供O(n)O(n)次合并的机会,而每次修改会对零碎的两块各提供一次机会,故总合并次数不超过O(n+m)O(n+m),因此当发生合并时直接重构并不会影响复杂度。

那么现在每块中的转换情况只可能是一条条互不相交的链,只需要记录每个初值转换后是什么,以及每个现值对应哪个初值即可。遇到查询的时候,我们需要知道零碎部分每个位置的值,不妨直接重构那两块,然后遍历一遍原数组aa即可得到每个位置的值。

在修改的时候,还需要同步维护bbcc数组,因为只涉及两个权值,因此暴力修改jj这一维也是可以承受的。

总时间复杂度O((n+m)\sqrt{n})O((n+m)n)

本条目发布于 2017年8月3日。属于 未分类分类。 作者是sensible

你可能感兴趣的:(2017多校联合)