1001. Big Integer
如果知道k−1个数的个数c1,c2,...,ck−1,那么方案数就是:
i=1∏k−1ci!(i=1∑k−1ci)!
构造多项式fi(x)=j=0∑nj!gi,jxj,则将这k−1个多项式乘起来之后,每一项乘以i!即可得到答案,可以用NTT在O(nk2log(nk))的时间内预处理出初始答案。
注意到我们只关心每个时刻答案的和,故可以在DFT意义下直接累加答案,最后再将结果IDFT回来。
对于单点修改操作,可以看成是给某个多项式叠加上一个只有一项系数不为0的多项式,观察NTT的公式:
yn=∑i=0d−1xi×(gdP−1)inmodP
那么它在DFT后的结果是一个等比数列,故直接对原始多项式叠加一个等比数列即可完成修改。
对于叠加多项式,直接O(k)重新计算乘积是不可取的,正确方法是对于每一项记录非0的乘积以及0的个数,单次修改时间复杂度为O(1)。每次操作的时间复杂度为O(nk)。
总时间复杂度O(nk2log(nk)+mnk)。
1002. Classic Quotation
首先通过KMP算法预处理出T的next数组,设:
prefi表示S的前缀i与T进行KMP后KMP的指针到达了哪里。
pregi表示S的前缀i中T出现的次数。
sufi,j表示从S的后缀i,从j指针开始KMP,能匹配多少个T。
那么前缀i拼接上后缀j中T的个数为pregi+sufj,prefi。
令preg为preg的前缀和,suf为suf的后缀和,si,j表示i前面中pref为j的个数,那么对于询问L,R:
ans=∑i=1L∑j=Rnpregi+sufj,prefi=(n−R+1)pregL+∑i=0m−1sL,i×sufR,i
以上所有数组均可以使用KMP和递推求出,时间复杂度O((n+k)m)。
1003. Counting Divisors
设n=p1c1p2c2...pmcm,则d(nk)=(kc1+1)(kc2+1)...(kcm+1)。
枚举不超过√r的所有质数p,再枚举区间[l,r]中所有p的倍数,将其分解质因数,最后剩下的部分就是超过√r的质数,只可能是0个或1个。
时间复杂度O(√r+(r−l+1)loglog(r−l+1))。
1004. Dirt Ratio
二分答案mid,检验是否存在一个区间满足r−l+1size(l,r)≤mid,也就是size(l,r)+mid×l≤mid×(r+1)。
从左往右枚举每个位置作为r,当r变化为r+1时,对size的影响是一段区间加1,线段树维护区间最小值即可。
时间复杂度O(nlognlogw)。
1005. Lazy Running
取w=min(d1,2,d2,3),那么对于每一种方案,均可以通过往返跑w这条边使得距离增加2w。也就是说,如果存在距离为k的方案,那么必然存在距离为k+2w的方案。
设disi,j表示从起点出发到达i,距离模2w为j时的最短路,那么根据dis2,j解不等式即可得到最优路线。
时间复杂度O(wlogw)。
1006. Logical Chain
对于每个询问,求出强连通分量,那么一个点数为t的强连通分量对答案的贡献为2t(t−1)。
利用Kosaraju算法,只需要在正反图各做一次DFS即可求出强连通分量。面对稠密图,Kosaraju算法的瓶颈在于寻找与点x相连且未访问过的点。考虑用bitset来保存边表gx,以及未访问过的点集S,那么只需要取出gx&S内的所有1即可在O(64n2)的时间内求出强连通分量。
时间复杂度O(64mn2)。
1007. Matching In Multiplication
首先如果一个点的度数为1,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为1的点。
那么剩下的图中左右各有m个点,每个点度数都不小于2,且左边每个点度数都是2,而右侧总度数是2m,因此右侧只能是每个点度数都是2。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。
时间复杂度O(n)。
1008. Phone Call
不难发现这个过程就是Prim算法求最小生成树的过程,用Kruskal算法同样正确。
将所有线路按代价从小到大排序,对于每条线路(a,b,c,d),首先把a到b路径上的点都合并到LCA,再把c到d路径上的点都合并到LCA,最后再把两个LCA合并即可。
设fi表示i点往上深度最大的一个可能不是和i在同一个连通块的祖先,每次沿着f跳即可。用路径压缩的并查集维护这个f即可得到优秀的复杂度。
时间复杂度O(mlogm)。
1009. Questionnaire
取m=2,必然存在一组可行解。
1010. Security Check
设fi,j表示仅考虑a[1..i]与b[1..j]时,最少需要多少时间。
若∣ai−bj∣>k,则fi,j=fi−1,j−1+1,否则fi,j=min(fi−1,j,fi,j−1)+1。
注意到后者只有O(nk)个,可以暴力DP,前者可以通过二分找到最大的t,满足i,j往前t个均不冲突,然后再从某个后者状态转移过来。
时间复杂度O(nklogn)。
1011. Time To Get Up
按题意模拟即可。
1012. Wavel Sequence
设fi,j,k表示仅考虑a[1..i]与b[1..j],选择的两个子序列结尾分别是ai和bj,且上升下降状态是k 时的方案数,则fi,j,k=∑fx,y,1−k,其中。暴力转移的时间复杂度为O(n4),不能接受。
考虑将枚举决策点x,y的过程也DP掉。设gi,y,k表示从某个fx,y,k作为决策点出发,当前要更新的是i的方案数,hi,j,k表示从某个fx,y,k作为决策点出发,已经经历了g的枚举,当前要更新的是j的方案数。转移则是要么开始更新,要么将i或者j继续枚举到i+1以及j+1。因为每次只有一个变量在动,因此另一个变量恰好可以表示上一个位置的值,可以很方便地判断是否满足上升和下降。
时间复杂度O(n2)。
1013. Yuno And Claris
先考虑如何求区间第k小值。对序列和权值都进行分块,设bi,j表示前j块中权值在i块内的数字个数,ci,j表示前j块中数字i的出现次数。那么对于一个询问[l,r],首先将零碎部分的贡献加入到临时数组tb和tc中,然后枚举答案位于哪一块,确定位于哪一块之后再暴力枚举答案即可在O(√n)的时间内求出区间第k小值。
接着考虑如何实现区间[l,r]内x变成y的功能。显然对于零碎的两块,可以直接暴力重构整块。对于中间的每个整块,如果某一块不含x,那么无视这一块;否则如果这一块不含y,那么只需要将x映射成y;否则这一块既有x又有y,这意味着x与y之间发生了合并,不妨直接暴力重构整块。因为有c数组,我们可以在O(1)的时间内知道某一块是否有某个数。
考虑什么情况下会发生重构,也就是一个块内发生了一次合并的时候。一开始长度为n的序列会提供O(n)次合并的机会,而每次修改会对零碎的两块各提供一次机会,故总合并次数不超过O(n+m),因此当发生合并时直接重构并不会影响复杂度。
那么现在每块中的转换情况只可能是一条条互不相交的链,只需要记录每个初值转换后是什么,以及每个现值对应哪个初值即可。遇到查询的时候,我们需要知道零碎部分每个位置的值,不妨直接重构那两块,然后遍历一遍原数组a即可得到每个位置的值。
在修改的时候,还需要同步维护b和c数组,因为只涉及两个权值,因此暴力修改j这一维也是可以承受的。
总时间复杂度O((n+m)√n)。