zoukankan      html  css  js  c++  java
  • 个人训练记录(UPD 9.16)

    本文章记录一些较难的题,摘自自己的blog中的其他文章。也有些单独成章有点浪费的题也写在里面了。

    2019.7.15-2019.7.21

    1182F(2900)

    题意:求在区间 ([a,b]) 中找一个最小的 x 使得 ( ext{abs}( ext{sin}(frac{p}{q} pi x))) 最大。 (0 le a le b le 10^{9}, 1 le p,q le 10^9)

    key:姿势

    实际上是求一个最小的 x 使得 (f(x)=2px mod 2q) 最靠近 q。

    性质:(f(x)+f(y)equiv f(x+y) mod 2q) 。所以对于区间内每个数 x ,都可以把它拆分为 (x=a+i*t+j) 。其中 (t=sqrt{b-a+1}, j=(x-a) mod t, i in [0,t])

    所以只需要把所有 (f(a+j)) 记下来排个序,之后枚举 i 二分查找即可。剩余的区间( (i=t) 时 j 可能取不到所有([0,t)) )需要暴力。

    有两个坑点:1. 有序表中同样 f 的取小的。 2. 因为模运算的序列是环形的,所以如果二分查找的结果是首/尾,需要特判。

    1194E(2200)

    题意:有 n 个线段,每个都是平行 x 或者 y 轴,只有互相垂直的两线段才会相交。问形成了多少个矩形。 (n le 5000, -5000 le x_i,y_i le 5000)

    key:树状数组

    考虑枚举矩形上边和下边,如果统计出与这两条边相交的竖线个数,那么就能知道贡献。先枚举下边,把所有与它相交的竖线插入树状数组。如果把竖线按照上端点的纵坐标排序,那么按照从下往上枚举上边时就可以删掉某些竖边。总复杂度 (O(n^2 log n))

    1194F(2500)

    题意:有 n 个题,第 i 个题花费时间是 (a_i)(a_i+1),概率都是 0.5,你只能顺着做。问 T 时间做题的期望个数。 (a_i le 10^9, T le 10^{14}, n le 2*10^5)

    key:概率

    总方案数是 (2^n), 考虑第 i 个题被做的方案数。令 (sum_i=sum_{jle i}a_i) ,那么剩余可随意支配的时间是 (T-sum_i),这些时间会被分配到前 i 个题上,每个至多分配 1,或者留给后面的题。后面的题不用管,就是 (2^{n-i}) 种方案。所以这个题的贡献是

    [frac{2^{n-i}*sum_{j=0}^{T-sum_i} {i choose j}}{2^n} ]

    分子是一个组合数第 i 行的前缀和,这个可以直接递推((sum_{ile m} {n+1 choose i} = 2sum_{ile m} {n choose i} - {n choose m}))。由于随着 i 增大, (T-sum_i) 变小,所以可以直接做。

    gym101630 A(银牌题)

    题意:有 n 个时刻,第 i 个时刻要么会在 (xi,yi) 生成一个半径为 yi 的圆,要么射击 (xi,yi) 这个点,如果该点在某个圆内则把对应圆删除并输出该圆的标号,否则输出 -1 。任意时刻圆之间不会相交(可以相切)。 (n le 2*10^5, -10^9 le x_i,y_i le 10^9, y_i > 0)

    key:线段树,结论

    结论:与一条竖线相交的圆的个数不超过 (O(log max y_i)) 个。

    证明:可以证明下图中,两圆的半径比是 4。也就是说最差时半径以4倍增长。

    1563212622108

    所以每次只需要找到覆盖该竖线的所有圆,然后一个个check即可。可以用线段树维护。

    当一个圆插入时,它覆盖的区间最大为 ((x_i-y_i,x_i+y_i)) 。以查找直线左边的圆为例,只要在 (x_i) 处把 (x_i+y_i) 插入,之后询问时递归直线左半边,如果区间最大值比 (x_i) 小则直接 return。递归到叶子时计算是否可行。

    因为至多递归到 log 个叶子,所以总复杂度是 (O(nlog n log max y_i))

    仔细观察可以发现实际上只有 i+k*m 这个集合中的点有边,并且形成了一个环,而环的大小就是集合中 1 的个数。

    gym101630 I(金牌题)

    题意:一个随机排列,偶数按顺序放到 e 数组中,奇数按顺序放到 o 数组中。每次可以询问 (e_i)(o_j) 的大小关系。求在 (3*10^5) 的询问数下输出 e 和 o 。 (n le 10000)

    key:排序,交互

    key:二分图

    考虑快排:每次把区间分为两段。

    枚举 o 中的每个数,此时考虑有若干段区间,该数一定存在在某个区间中间(或者是 1 或 n,这个要特判)。由于区间之间存在有序性,所以可以先对所有区间二分,此时该数只会落在两个区间内,然后再从两个区间里暴力判断。由于是随机排列,所以可以证明总询问数是 (O(n log n))

    1190E(3100)

    题意:二维平面上有 n 个点,你可以放至多 m 条直线使得 (0,0) 与每个点的连线至少与一条直线相交。求原点与所有直线的距离最小值最大是多少。 (n,m le 2*10^5,-10^5le x_i, y_i le 10^5)

    key:二分,贪心

    二分答案,此时可以以原点画圆,那么每条直线都可以与该圆相切。每个点与该圆的两切点形成一个区间。问题变成对于 n 个环形区间,用至多 m 个点去覆盖使得每个区间都被覆盖,问是否可行。

    考虑序列:经典问题。首先要把包含关系去掉,之后按左端点排序,每次贪心地选右端点,直到该区间没有被覆盖时选下一个点。

    去掉包含关系:先按左端点排序,相等则按右端点。此时倒着扫,按右端点严格递减的顺序选择区间。此时的包含关系只有可能是左端点相同,但这并不影响贪心策略的正确性。

    回到原题。首先看如何处理环形区间:储存时要保证左端点<右端点(越界不管),然后复制二倍,然后去除包含关系。之后再把每个区间的第一份拿出来,再复制二倍。

    之后可以用双指针贪心地找到:如果选择了当前区间的右端点,那么下一个没有被覆盖的区间的标号。

    用倍增可以得到后 (2^k) 个区间的标号。

    然后枚举左端点,考虑以它为开头跳 m 步是否覆盖完即可。总复杂度 (O(Dn log m)) ,D是二分次数。

    1157G(2800)

    题意:有一个 (n cdot m) 的 01 矩阵,每次操作是每行或每列 01 翻转,问是否存在一个解使得矩阵中的元素是非降的。 (n,m le 200)

    key:结论

    思路是 tutorial 中牛逼的老哥给的

    考虑最终解的情况:当 (n ge 2) 时,要么第一行全 0 ,要么最后一行全 1 。所以对于给的矩阵,可以先把它变成这样(也就是说可以枚举对列的情况,只有两个:把第一行变全 0 或者把最后一行变全 1)。此时只需要看看行的情况就行,这个是非常简单的。

    仔细思考发现其实这个是充要条件。因为如果列的情况有第三种并且得到一个合法解,那么行的操作也一定会使要么第一行全 0,要么最后一行全 1,实际上造成的影响是一样的。

    并且 n=1 是平凡的,并不要特判。

    1175F(2600)

    题意:长度为 n 的序列,求有多少个区间 ([l,r]) ,使得其构成了一个 1~r-l+1 的排列。 (n le 3*10^5, a_i le n)

    key:随机权值

    合法区间中肯定包含 1 ,并且最大值就是长度。先考虑最大值在 1 的右边的情况,可以枚举每个 1 的右边,记录最大值 x ,此时需要检查 ([r-x+1,r]) 这个区间是否是一个排列。最大值在 1 左边的情况同理。

    考虑 hash :给 1~n 的每个数字一个 64 位的随机权值,定义为 (h_i) ,这样一个长度为 k 的排列的 hash 必须是 (h_1 xor h_2 dots xor h_k) ,这样就可以 O(1) check 了,所以复杂度是 O(n)。

    2019.7.22-2019.7.28

    hdu6578(金牌题)

    题意:给长度为 n 的序列染 4 种颜色,有 m 个限制,每个形如 ([l_i,r_i]) 区间内恰好有 (x_i) 种颜色。问合法方案数。 (n,m le 100)

    key:dp

    其实思路很简单,主要问题在于如何记录状态才能表示出这个限制。考虑一个区间内的颜色种数怎么表示:首先这个区间内必须已经染色,其次每种颜色只贡献 1 。后者可以用该颜色最后一次出现位置表示。

    所以 (f_{i,j,k,l}) 表示四种颜色最后一次出现的位置。为了表示方便可以给他们排个序:为四种颜色最后一次出现位置排序后分比为 (i, j, k, l, i<j<k<l) 。这样每次就可以检查以 l 为结尾的所有区间是否合法了。 复杂度 (O(n^4+n^3m))

    hdu6586(银牌题)

    题意:给一个字符串,求长度恰好为 k ,每个字符出现次数在 ([l_i,r_i]) 内的字典序最小子序列。 (sum |S| le 3*10^5)

    key:贪心

    没有出现次数的限制就是之前计蒜之道的那个题,这个题的限制更强一些。

    考虑当前已经构造出了一个前缀,当前位置在 p ,考虑下一个字符应该选哪个。肯定是位置在 p 之后,字典序尽量小,并且选了它之后可以构造出符合要求的答案。

    由于同种字母肯定选出现位置最靠前的,所以待选位置至多只有 O(m) 个,而做一次check也是 O(m) 的。所以复杂度是 (O(nm^2)) ,其中 m 是字符集,即26

    hdu6588(金牌题)

    题意:求

    [sum_{1le i le n} gcd(lfloor sqrt[3]{i} floor,i), n le 10^{21} ]

    key:推导

    主要是傻逼了,卡了奇怪的地方……

    枚举三次根号后的那个值,(lfloor sqrt[3]{n} floor) 特判。很容易推到

    [sum_{dle m-1} phi(d) sum_{ile (m-1)/d} lfloorfrac{(id+1)^3-1}{d} floor - lfloorfrac{(id)^3-1}{d} floor ]

    然后后面那个看起来很难搞的东西只要展开就好了………………然后就得到了一个 (O(n^{1/3})) 的做法

    hdu6589(金牌题)

    题意:给一个序列 a ,m 次操作,每次是求 $b_i = sum_{j = i - k cdot x} a_j mod 998244353 (0 leq x, 1leq j leq i) $ ,然后用 (b_i) 替换 (a_i) 。求最终序列。 (n le 10^5, m le 10^6, k in {1,2,3})

    key:ntt

    题解里写的非常清楚…………

    首先生成函数分析一下: (sum b_ix^i=(sum x^{ki})(sum a_ix^i)) ,所以满足交换律,顺序没有关系。问题在于求 ((sum x^{ki})^n=sum {n-1+i choose i} x^{ki}) 。然后就做完了。

    hdu6602(银牌题)

    题意:长度为 n 的序列,找一个区间使得每个元素要么不出现,要么出现次数 (ge k)(n,k,a_i le 10^5)

    key:线段树

    其实是个套路题……

    考虑单个元素,固定左端点时,可行的右端点是两个区间。多个元素则就是取交集中最靠右的右端点。只需要把每个元素对应合法区间+1,每次查询最靠右的最大值位置即可。

    hdu6598(金牌题)

    题意:把 n 个人分到两个集合中,有 m 对人 ((u_i,v_i)),若均为 A 类那么得到 (p_i) 的收益,均为 B 类得到 (q_i) 的收益,否则得到 (r_i) 的收益,且保证 (r_i=p_i/4+q_i/3)。求分类的最大收益。 (n le 500, m le 10000)

    key:最小割

    是个套路……

    考虑用总和减去最小损耗,损耗对应一个割。最终与源点/汇点相连则认为其属于哪一类,那么容易得到以下方程:

    [a+b=p+r \ c+d=q+r \ a+e+d=p+q \ b+e+c=p+q ]

    然后就能解出一组解 (e=(p+q-2r)/2,a=b=(p+q)/2, d=e=(q+r)/2)

    由于 r 满足那个性质,所以 e 大于 0,所以就可以用最小割做了。

    2019.7.29-2019.8.4

    hdu6613(金牌题)

    【hdu6613】Squrirrel 树形DP

    nowcoder885E(银牌题)

    题意:求给定图的所有生成子图的最大独立集大小之和。 (n le 26) ,空间限制100mb

    key:dp

    主要是这题卡空间我楞了一下,实际上是傻逼题,于是就银牌题吧

    直接dp,套路 CF1105E 点这里,然后ctrl+f搜独立集 。数组开 char 解决空间限制。 (O(2^n))

    nowcoder885F(金牌题)

    题意:给 n 个互不相同的数,找出一个集合,使其任意两个数的二进制表示至少两位不同。 (n le 5000)

    key:独立集

    考虑如果两数二进制表示至少两位不同则连边,即求最大团,即求反图的最大独立集。

    考虑反图:即两数二进制恰好一位不同则连边,这样由二进制中 1 的奇偶性分成了两部分,即二分图。用求二分图独立集的方法即可。

    下面讲如何构造一组解:

    众所周知,二分图最大独立集=总点数-最小点覆盖。即删掉最小点覆盖,剩下的点一定是最大独立集。考虑最小点覆盖如何构造:从左部点的所有非匹配点开始找增广路。这样的增广路一定是非匹配边和匹配边交替的。最终所有访问过的右部点和未访问过的左部点构成最小点覆盖。所以其余点构成最大独立集。

    min_25

    min_25筛学习笔记

    写了 luogu 和 loj 俩板子题,就不再多水经验了

    一个应用是它的预处理,可以做小于等于 n 的所有质数的多项式函数之和。

    hdu6611(银牌题)

    题意:给一个序列,一个非降子序列的权值为序列中的元素和。求拿出 k 个不相交非降子序列使其权值和最大。 (n le 2000,k le 10)

    key:费用流

    费用流建模很显然。主要是存了一个《挑战》里的费用流板子,而且发现两个有趣的事情:

    1. dij 的小根堆不要用第一维取负号的方法,会变慢。要用声明比较长的那个
    2. 用 vector 存图比手写链表要快,原因不明(感觉上是遍历边的时候编号不连续,访存很慢)。本地开 O2 的情况下时间差了七倍。

    CF1198D(2400)

    题意:(ncdot n) 的棋盘上有若干个格子是黑的,剩下的是白的。你每次操作是选定一个 (wcdot h) 的子矩阵把它染白,花费是 (max(w,h)) 。问最小代价使其全部变白。 (n le 50)

    key:区间dp

    主要是怎么想到是区间 dp 的。

    首先发现一定是若干个正方形,且任意两个正方形不能相邻。现在问题在于如何把这样的正方形合并成一个大的使其代价更低。这样就想到可以枚举一个子矩阵,然后枚举横着的中间点/竖着的中间点。所以复杂度是 (O(n^5))

    CF1198E(2500)

    题意:(ncdot n) 的棋盘上有 (m) 个子矩阵是黑的,剩下的是白的。你每次操作是选定一个 (wcdot h) 的子矩阵把它染白,花费是 (min(w,h)) 。问最小代价使其全部变白。 (n le 10^9,m le 50)

    key:最大流

    显然每次一定是找一横行或者一竖列。对于子矩阵,离散化之后可以看做 (100cdot 100) 的棋盘上的若干格子。于是就变成了经典的二分图最小点覆盖。由于离散化之后带了权,所以不如直接跑最大流。

    2019.8.5-2019.8.11

    hdu6625(金牌题)

    题意:给两个序列,可以任意打乱顺序,求对应位置的异或值序列字典序最小是多少。 (n le 10^5, a_i < 2^{30})

    key:trie,牛逼套路,贪心

    先说一个naive的贪心:在两个 trie 上 dfs ,要保证相同子树尽量合并,此时只会剩下若干个 a 中的值或者若干个 b 中的值,回溯时收集两个子树中剩余点集,能合并就合并。由于每次只会在两个点集上尝试合并,如果每次是暴力较小的一侧,在较大的一侧形成的trie中查询,那么每次在trie上查询都会消掉一对点,所以dfs+查询的复杂度是 (O(nlog n))

    题解给了一个牛逼的做法,甚至有了可拓展性:

    首先假设元素互不相同(存在相同元素时只需记录出现次数),假设 (f(x,y)=x xor y) ,那么所有的 (f(a_i,*)) 互不相同,所以必定存在最小值。同理 (f(*,b_i)) 也是。如果把它看成二分图,那么每个点都向另一侧有一条边指向取最优时的结果。

    如果每次随机选取一个点开始dfs,那么一定不会形成一个大于 2 的环(因为如果存在 a->b, b->c,这意味着 f(b,c) 优于 f(b,a) ,形成大于 2 的环必然矛盾)。由于搜下去一定会终止,那么此时一定找到一个二元环,可以把它拿出作为一个最优匹配。之后再找最优即可。

    具体地说,维护一个栈。当栈为空时,随便丢进去一个点,寻找它的 f 最优解。继续做下去,当栈顶元素找到的最优解已入栈,说明它一定在栈顶第二个,弹出并计算答案。

    由于每次寻找最优解都会使一个元素入栈/使两个元素弾栈,所以操作次数是 (O(n)) 的(有重复元素时也是如此)。对于此题,复杂度即 (O(nlog MAXV ))

    考虑上述的分析过程:几乎没有用到该题的特殊性质。对于普遍情况,只需要满足:

    1. (f(a_i,*), f(*,b_i)) 互不相同
    2. 寻找 (f(a_i,*), f(*,b_i)) 的最优解可以快速完成

    就可以沿用该算法。复杂度是 (O(n*find\_optimzal))

    hdu6633(金牌题)

    题意:有一个长度为 2*n 的未知排列,维护一个集合,初始集合包含前 n 个元素。每次会弹出一个最小的或者最大的,并把下一个元素加入。给出所有弹出的元素和最大还是最小,求构造一个字典序最小的合法2*n排列。 (2*n le 10^5)

    key:贪心

    考虑处理出每个点可以放的数字集合,这么想可能很难,那么考虑处理处每个数字可以出现的位置,此时的限制就是数字之间的了。

    首先对于一个已被弹出的元素,它一定在[1,i+n-1] 中出现。非弹出序列中的元素,一定在[1,2*n]中出现。
    然而左端点不一定是 1 。如果对于 x ,如果第 i 次弹出的最小值比它大,那么它一定出现在 n+i 及之后。对于最大值同理。

    这样我们就处理出一个比较紧的界,可以证明是充要的。

    实际上这种题型之前做过( 点我搜索2018-2019 XIX Open Cup, Grand Prix of Korea 的 D 题),然而这次要求字典序最小。

    对于可行解,可以直接从前往后,每次放当前可行集合中右端点最小的。原因是如果随便放,那么在未来可能出现多个元素同时到达了右端点,此时需要在前面找元素顶掉

    然而我们发现,对于这个题,右端点除了 2*n ,其余的至多只有 1 个,并不会出现这种情况。所以只需要每次拿最小元素,如果有一个元素到达了它的右端点那么再放它即可。

    nowcoder888 F(金牌题)

    题意:二维平面上 n 个点,求有多少个有序四元组满足其中一点被另外三点形成的三角形严格包含。 (n le 1000, |x_i|,|y_i| le 10^9)

    key:计算几何

    枚举中间点,对于另外三个点,它们组成了一个三角形。如果固定其中一个点来看,另外两个点与中间点连线的延长线形成了一段区间,正好包含剩下的那个点,并且夹角小于180度。现在问题变成求所有合法区间的区间和,“合法区间”指左右边界都在某条延长线上的开区间,并且区间长度要小于180度。

    于是只需要把每个点与中心点的连线以及延长线记录下来。由于存在三点共线,所以对于一条线,记录它一侧上有多少个点,记录对侧有多少个点。此时直接枚举左端点,右端点递增,然后用各种前缀和搞一搞就行了。

    由于坐标范围很大,所以double会挂,所以只能用点坐标的方式记录角度。此时极角序排序要先排象限,再用叉积。由于这么实现的长度比较大(比double还大),所以尽量用int

    CF1198F(2900)

    题意:给 n 个数,把它们划分成两个非空集合,使得每个集合内部所有数的 gcd 为 1,求一个分组方案。 (n le 10^5,a_i le 10^9)

    key:随机

    假设一个数已经被选了,那么就需要用其他数来把质数消掉,那么至多需要 k=9 个。换句话说,我们只需要关注 2*(k+1) 个数字即可。于是可以先随机两个数是不同组,再用状压DP记录两组中哪些质数没有被削掉。

    有一个随机:每次random_shuffle,然后强制第一个数字是第一组。每次顺着取gcd,当gcd下降时取到第一组。剩余数字是第二组。虽然看起来很简单,但是真的好难卡,于是它真的跑过了……(因为这种方法保证其中一组是合法的,所以要卡掉它问题在于如何使另一组不合法。即构造2*(k+1)个数字,使得只有一组划分方案,这样他就很难随机到了。)

    nowcoder887 K(金牌题)

    题意:抽象完之后是求一个函数的前缀和。这个函数在 p^k 且 p 模 4 余 1 处的函数值是 3k+1 ,其他位置是 1。 (n le 10^9)

    key:min_25,打表

    min_25:本质上可以用板子上那个式子来推。需要注意的是这里 p 只能是模 4 余 1 的,所以要改一下板子。

    打表: 就单纯的打表……不要用分解质因数那个东西,跑的巨慢

    2019.8.12-2019.8.18

    gym101485 B(金牌题)

    题意:有 n 个区间,分成 p 组,每组的权值是所有区间交的长度,交不能是 0 。求权值和最大。保证有解。 (n,p le 200)

    key:思路,dp

    对于每一组,限制区间交的长度的区间至多是 2 个(认为 {[1,3],[2,4],[2,3]} 只有 [2,3] 限制)。考虑一个分组方案,定义这种区间是好的,其他区间(即不限制它所在组的权值)是坏的。

    显然为了使权值最大,每个区间都趋向于成为坏的。

    1. 限制该组权值的区间只有 1 个。此时其他区间一定都包含它。
    2. 限制该组权值的区间有 2 个。取其中一个为 a,那么 a 一定不包含任何其它区间,因为如果它包含其它区间,那么它可以分配到它包含的某个区间所对应的组,成为一个坏的,此时会使答案更大。

    所以对于一个包含其它区间的区间,它要么被分配到它包含的某个区间的组内(此时不影响答案),要么单独成为一组(成为第一类),所以可以按这个性质进行分类。

    具体地说,先把给定的 n 个区间划分为两类:如果该区间不包含任何其它区间,分到 A 类。否则分到 B 类。

    A 类中的区间没有包含关系,所以排序后的分组一定是一段段区间,这个可以 DP (甚至因为决策单调性可以做到 (O(n^2))

    B 类中的区间要么单独成组,要么不影响答案,所以只需要排序取长度前若干大即可。

    hdu6660 (金牌题)

    题意:在 (n cdot m) 的矩形上,随意指定起点遍历每个格子恰好一次,每次只能走到与当前点的欧拉距离大于1小于3的点,求一组方案。 (n,m le 100)

    key:构造

    赛后补题,跟google那个差不多。

    写个暴力,发现 (1cdot n (n ge 2), 2cdot 2) 没解,其他的都有解。暴力可以构造出 (2 cdot 3, 2 cdot 4, 2 cdot 5) 的解并且可以连起来得到 (2 cdot n) 的解。用 (2 cdot 3) 可以构造 (3 cdot n) 的解

    CF 906D(2600)

    题意:给一个序列,定义函数:

    [f(l,r) = egin{cases} a_l^{f(l+1,r)} & ext{$l < r$} \ a_l & ext{$l = r$} end{cases} ]

    给定 (a_i, m) ,每次询问 (f(l_i,r_i) mod m)(n le 10^5,m le 10^9,q le 10^5)

    key:降幂公式

    属于复习题

    [gcd(a,p)=1: a^bequiv a^{b mod phi(p)} pmod p \ gcd(a,p)>1:left{ egin{aligned} b < phi(p) &: a^bequiv a^{b} pmod p \ b ge phi(p) &: a^bequiv a^{b mod phi(p) + phi(p)} pmod p \ end{aligned} ight. ]

    可知实际上是迭代求欧拉函数。由于这东西的迭代次数是log的,所以直接暴力即可。

    注意 gcd 不为 1 时要注意大小关系,这里要先判断再决定是否加上去。

    nowcoder889 I(金牌题)

    题意:求 (sum_{k=1}^nkM&M)(n le 10^{18},M le 10^{11})

    key:类欧

    按位考虑,实际是求 (sum_{k=1}^n[kM ext{的二进制第i位是否为1]}) 。这个实际上就是个类欧,就很厉害

    [sum_{k=1}^n[kM ext{的二进制第i位是否为1]} \ =sum_{k=1}^n[kM >>i & 1] \ = sum_{k=1}^nlfloorfrac{kM}{2^i} floor-2*lfloorfrac{kM}{2^{i+1}} floor ]

    注意中间需要用__int128

    李超线段树学习笔记

    李超线段树学习笔记
    回头再加两个题

    线性齐次递推式

    线性齐次递推式快速求第n项 学习笔记

    2019.8.19-2019.8.25

    hdu6682(金牌题)

    题意:给 n 个数,定义 (f(x)) 为数字 4 在 x 的十进制表示中的出现次数。求 (sum_{S} f(sum_{i in S}a_i))(n le 40,a_i le 44444444)

    key:思路

    考虑把前一半和后一半的组合情况统计好了,现在问题变成给两个数组,问两边各取一个数字求和的 f 之和。

    最大就 10 位,所以考虑按位统计。假设当前考虑第 k 位,那么如果两个数组中的数字按照 mod 10^k 排序,那么 a 数组中的每个元素对应了 b 数组中的两个区间(即相加为 4.... 和 14.....)。

    直接做是 (O(10*2^{n/2}n)) ,注意到如果 k 从低向高枚举,那么其实是计数排序的过程。如果 a 和 b 都有序,那么 a 中随着 i 的增大,两个区间在 b 中只会向前,可以用双指针来扫。所以复杂度是 (O(10*2^{n/2}))

    hdu6683(金牌题)

    题意:求序列 (1,2,...,n) 的等比子序列个数。 (n le 5*10^{17})

    key:推公式

    长度小于等于 2 的直接算,现在看大于 3 的。

    假设公比是 (a/b) ,满足 a>b 且 gcd(a,b)=1,假设长度为 k ,第一项是 A ,那么最后一项是 (Afrac{a}{b}^{k-1}) 。由于这个是整数,那么一定要满足 (b^{k-1}|A) 。所以 (a^{k-1}) 必是其因子。由于 b<a ,所以确定 a,b,k 和最后一项之后,该等比子序列也随之确定,并且是唯一的。

    所以等比数列个数是

    [sum_{a=2}^n phi(a)lfloor frac{n}{a^{k-1}} floor ]

    k>3时直接暴力,k=3 时可以证明 (lfloor frac{n}{a^2} floor) 只有三次根号个,所以就数论分块+杜教筛。

    hdu6686(金牌题)

    题意:给一棵树,定义(a,b) 合法当且仅当树中存在一条长为 a 的路径和一条长为 b 的路径,且两路径不相交。求合法点对的个数。 (n le 10^5)

    key:树的直径,st表

    题解做法:直径的两个端点一定都被使用了。对于每个 a 找到最大的 b ,分情况讨论一下,能搞到 O(n)

    我的做法:实际上所有的选法一定是【小于等于子树内直径的所有数】和【小于等于子树外直径的所有数】的笛卡尔积。对每个 a 找最大的 b,实际上是一个序列前缀取 max 的操作,操作完后询问每个点的答案,可以用 st 表实现。而这两个东西可以用两遍树形dp实现。

    hdu6687(金牌题)

    题意:给 n 个数 (a_i) 和 n 个数 (b_i) ,要使两两配对,权值是 (a_i xor b_j) ,求权值和最大是多少。 (n le 10^5)

    key:trie,套路

    跟今年多校第五场的 B 一模一样,用那个套路的话只需要把找异或最小值改为找最大值即可。

    传送门:https://www.cnblogs.com/dqsssss/p/11312849.html

    hdu6712(金牌题)

    题意:有一个三维无限大方格平面,每个格点上有权值 1 。修改 m 个形如 (a_{n, x_i, y_i}) 点的点权为 (v_i) 。之后每秒将改 (a_{i,j,k}) 将变为 (a_{i+1,j+p,k} ^ {t1} imes a_{i+1,j,k+q} ^ {t2} imes a_{i+1,j,k} imes a_{i,j,k}) 。求 (n) 秒之后 (a_{0,0,0} mod 998244353) 的值。 (t1,t2,p,q,n le 10^9,m le 10^5)

    key:中国剩余定理,卢卡斯定理

    考虑每个点的贡献。相当于有一个三元组 (x,y,z) ,每次 x 减 1,y 可以不变或者以 t1 的权值减 p,z 可以不变或者以 t2 的权值减 q,一条路径的权值是每步的权值之积。问从 ((x,y,z)) 走到 ((0,0,0)) 的所有路径权值之和,该值作为 (v_i) 的幂次乘给答案。

    实际上一条路径的权值是一定的,所以问题在于求路径条数。这个是 (frac{n!}{a!b!c!}) 的形式。即求 (frac{n!}{a!b!c!} mod (998244353=2^{23} imes 7 imes 17))

    考虑分开,最后 crt 合并。7 和 17 的答案可以直接换成组合数用卢卡斯定理算,现在考虑阶乘模 (2^{23}) 怎么做。由于涉及到求逆,所以要把答案表示成 (n!=A imes 2^B) 的形式。

    (n!=1 imes 2 imes 3 imes 4 imes 5 imes ... imes n = (1 imes 3 imes 5 imes ...) imes 2^{n/2} imes (1 imes 2 imes 3 imes ... imes n/2))

    上面的除都是整除。

    所以就预处理 (1 imes 3 imes 5 imes ...) ,这个显然关于 (2^{22}) 是循环节(因为 (2^{23}+1 mod 2^{23} = 1)),所以就直接递归。

    代码对于 7 和 17 没有用卢卡斯定理,也是用的同样的思路算的阶乘。不过这里要算一个形如 (((p-1)! mod p)^m) 的东西,根据威尔逊定理这东西只与 m 个奇偶性有关,所以可以 (O(1)) 计算,少个 log。

    要 fread 读入优化,hdu卡常。把好多中间变量去掉了居然就过了,神奇……

    2019.8.26-2019.9.1

    补考,咕咕咕

    2019.9.2-2019.9.8

    gym102012 D(金牌题)

    题意:给 n 个数 (a_i)(n imes n) 的 01 矩阵 M ,a 序列中每个元素小于等于 n 。一个合法的子序列定义为相邻两项 (a_i,a_{i+1})(M[a_i][a_{i+1}]=1) ,求所有合法子序列出现次数的三次方之和。 (n le 200)

    key:牛逼dp

    非常重要的转化:三次方等价于三个相同的 a 序列的公共子序列三元组数。所以有一个显然的dp:第一个序列匹配到第 i 个,第二个序列匹配到第 j 个,第三个序列匹配到第 k 个,要满足这三个位置的元素相同。

    方程: (f_{i,j,k}=1+sum_{i'<i,j'<j,k'<k}f_{i',j',k'} [M[a_{j'}][a_i]=1])

    然后发现与 i' 和 k' 无关,所以对另外两维做前缀和。对于 j',只需要把小于 j 且满足那个矩阵的东西做前缀和即可。复杂度 (O(n^3))

    gym102012 H(金牌题)

    题意:n 个线段,k 个颜色,求一组染色方案,使得被 k 种颜色都染色的长度最长。 (T le 1000,n ,kle 2*10^5, sum n le 2*10^6)

    key:贪心

    按左端点排序,扫描线,每次维护当前段的所有颜色。每加入一个线段,如果颜色还剩下那就用,否则就继承所有区间中右端点最小的区间的颜色。

    坑点:由于只给了 (sum n) ,所以要判n<k

    luogu4198

    题意:有 n 个楼,在横坐标为 i 的位置有一个高度为 (h_i) 的楼。每次单点修改高度,询问在 (0,0) 能看到几个楼。 (n le 10^5)

    key:线段树,姿势

    主要是一个姿势:线段树维护单调栈。上面可以维护各种信息。

    对于这个题,即询问所有斜率倒着做单调递减的栈的栈大小。

    在每个区间记录当前区间的单调栈大小,考虑合并:只需要比较左右区间的最大值。如果左区间的最大值比较大,那么右区间没有贡献;否则就要弹掉右区间单调栈中比左区间最大值要小的元素。

    记 f(l,r,x) 表示在区间 [l,r] 中加入元素 x 形成的单调栈的大小,那么上面两种情况就对应两个式子。
    对于前者,只会往左边递归。对于后者,需要递归一个 f(mid+1,r,lmx) ,这个东西可以预处理出来。

    考虑修改,每次修改只会动 log 个节点,对于每个节点只会修改 log 个预处理的 f(mid+1,r,lmx) 。

    复杂度和李超树分析是一样的,是 (O(nlog^2n))

    计蒜客t41388(金牌题)

    题意:给一棵 n 个点的树,有点权。q 次询问,每次问距离点 x 距离小于等于 k 的点权和。 (n le 10^6,k le 100, q le 5000)

    key:dfs,树状数组

    虚树不对,长链剖分对,但我不会……
    cf1076E 的做法基本相同。定义 f(v,k) 为 v 子树中与 v 距离小于等于 k 的点权和,这个可以离线后维护一个树状数组,树状数组以深度为下标,即求子树中深度在 [deep[v],deep[v]+k] 的所有点的点权和。这个可以在第一次进入子树时减掉子树外为这个深度的点权和,回溯时加上所有的这个深度的点权和,既得子树内的答案。

    对于每个询问,拆成 O(k) 个:f(x,k)+f(fa[x],k-1)-f(x,k-2)+f(fa[fa[x]],k-2)-f(fa[x],k-3)+...

    总复杂度 (O(n log n + qk log n))

    gym101955(x牌题)

    题意:n 个人的约瑟夫环,数到 k 出队,问第 m 个出队的人的编号。 (m le n le 10^18, min{k,m} le 2*10^6)

    key:数学

    来说一个约瑟夫问题的通用解法:

    考虑给 n 个人标号为 0...n-1,第一个出队的人是 k-1,那么从 k 开始报数 1 ,此时得到一个长度为 n-1 的约瑟夫环,为一个子问题。环上标号为:(k, k+1, k+2, dots, n-1, 0, 1, 2, dots, k-2),将其对应到 (0,1,dots,n-2) 可得:(q = (p+k) mod n),其中 p 为子问题答案,q 为映射后的标号。

    所以可以从子问题开始往上做。考虑最后一个子问题:n-m+1 的约瑟夫环中第 1 个出队的人的编号。显然是 k-1 ,再加个取模即 ((k-1) mod (n-m+1)) 。于是可得一个暴力代码:

    s = (k-1)%(n-m+1);
    for(LL i = n-m+2;i <= n;i ++)
        s = (s+k)%i;
    

    这是 (O(m)) 的。考虑可能 (i) 比较大,一次可能加好多个 k 才会取模,所以可以加一个优化

    s = (k-1)%(n-m+1);
    for(LL i = n-m+2;i <= n;) {
        LL t = min(n-i+1,(i-s-1)/k);
        if(t==0) {
            s = (s+k)%i;
            i++;
        }
        else {
            s = (s+k*t)%i;
            i += t;
        }
    }
    

    然后发现跑得很快,除了 k=1,所以 k=1 要特判。

    2019.9.9-2019.9.15

    hihocoder1872 (金牌题)

    题意:定义 (a,b,c) 为一组勾股数,c 是斜边长度,且 (a,b,c) 与 (b,a,c) 看做一组。问 c<=n 的三元组有多少对。 (n le 10^9)

    key:推公式

    我真的是惊了,为什么要出这种带了板子就会的题。

    首先你要知道勾股数的构造才能做这个题,即 (a=m^2-n^2,b=2mn,c=m^2+n^2) 。所有勾股数不好算,所以我们对本原勾股数计数,乘上个倍数即可。在上式中, ((a,b,c)) 为一组本原勾股数当且仅当 $gcd(m,n)=1 ext{且 (m,n)为一奇一偶}$ 。

    (f(n)) 为以 n 为斜边的三元组对数, (g(n)) 为以 n 为斜边的本原勾股数个数,(F,G) 为对应的前缀和,那么有:

    [f = g imes 1 o F=sum_{1 le ile n}G(n/i) \ G(n) = sum_{1le xle N} sum_{1 le y le N} [x^2+y^2 le N][gcd(x,y)=1][ ext{x is odd, y is even}] ]

    然后就对 G 随便推一波就行了。形式是一个调和级数。

    不预处理的话复杂度是 (O(Tn^{3/4}ln n)),预处理前根号下三分之二次方的复杂度就变成 (O(Tn^{2/3}ln n)) 。预处理就直接用上面给出的这个 G ,随便积分一下算出来预处理前 B 项的复杂度是 (O(B ln B)) 。实测了一下大概取 (10^7) 比较优,十组 (10^9) 只跑 0.6s 左右。

    hihocoder1875(金牌题)

    题意:给一个有向图,定义 (u,v) 合法当且仅当 u 能走到 v,权值为 u^v。Q 次询问每次问第 k 大的权值。 (n le 5*10^4,m le 2*10^5 , Q le 10,T le 3, k le 10^9)

    key:bitset

    容易想到二分答案,问题在于 check 。如果我们处理出来点 x 能走到哪些点,设这个集合是 S,那么就要找有多少个 (y in S) ,使得 x^y>mid。这个是在 trie 上做的。所以就不用二分答案了,直接在 trie 上贪心。

    考虑从 trie 上走的过程:贪心选 0,如果不行,那么走 1。判断是否不行是用子树和,这就是值域上的区间和,所以现在问题变成查询标号小于等于一个数的个数。

    所以有一个 bitset 的做法。用 tarjan+拓扑排序 预处理 bitset 的复杂度是 (O((n+m)n/W)),模拟 trie 上的复杂度是 (O(n^2/Wlog n)) ,后者应该是不行的。

    所以手写 bitset ,对 n/W 位建一个前缀和。后者的复杂度为 (O(n log n)) ,需要用到 __builtin_popcountll 。

    所以这个题的总复杂度为 (O(T((n+m)n/W+Qnlog n))) ,空间复杂度为 (O(n^2/W))

    (这里手写了 bitset ,由于差分的性质所以改成前缀和,由于 __builtin_popcountll 而优化了查询的复杂度。如果不是二进制 1 的个数那么查询的复杂度要再乘一个 W,如果不满足差分性质可以在 bitset 上分块)

    hihocoder1876(金牌题)

    题意:给一个 n 次多项式 F,求一个 n 阶多项式 G,使得 G 的每一个根(不一定实根)为 F 的对应根的 m 次幂。 (n+m le 10,|a_i| le 120),保证 G 的系数 (< 10^{12})

    key:牛顿恒等式

    设多项式 (F(x)=sum_{0le i le n} a_ix^i) ,设其所有根为 (x_1,x_2...x_n) (包括复根) ,设 (S_k=sum_{i=1}^nx_i^k)(b_i=a_{n-i}) 。则对于任意的正整数 k ,有

    [sum_{i=1}^kS_ib_{k-i}+k imes b_k=0 ]

    所以只要用系数递推出 n*m 个 S,然后反推出答案即可。因为 (SG_i=SF_{i cdot m})

    nowcoder881D(金牌题)

    题意:给 n 个 m 维向量 (v_i=(a_{i,1},a_{i,2},dots,a_{i,m})) ,定义 (F(x)) 为有多少个 (v_i) 使得其每个元素与 x 的二进制 and 均为奇数个 1。求 F 序列。 (n le 10^5,m le 10,k le 20,0le a_{i,j} < 2^k)

    key:集合幂级数

    用大写字母表示集合,则

    [f(x)=sum_{1le i le n} prod_{1 le j le m} [a_{i,j} and x ext{的二进制表示有奇数个1}] \ = sum_{1le i le n} frac 1 2^m prod_{1 le j le m} 1-(-1)^{|A_{i,j} and X|} \ = frac 1 2^m sum_{1le i le n} sum_{S} (-1)^{|S|+sum_{j in S}|A_{i,j} and X|} \ = frac 1 2^m sum_{1le i le n} sum_{S} (-1)^{|S|+|X and oplus_{j in S}A_{i,j}|} \ = frac 1 2^m sum_{1le i le n} sum_{S} (-1)^{|S|}(-1)^{|X and oplus_{j in S}A_{i,j}|} ]

    如果把 (oplus_{j in S}A_{i,j}) 看做一个序列的下标,那么这就是这个序列的 fwt 之后的结果,每个元素是用前面两个求和统计的。

    具体地说,对输入的每个向量求子集异或和,以此为下标在对应位置加上 ((-1)^{|S|}) ,做 fwt ,最后再除以 (2^m) 就是答案。

    gym102307H(金牌题)

    题意:给三个长度为 n 的字符序列,它们可以生成一个序列,每个位置的元素是原先三个序列对应位置中任选一个元素,这个序列的权值是它的 127 进制 hash 值取模。求最小的权值。 (n le 28)

    key:折半

    就是问两个序列中任取两个元素相加取模最小是多少,跟 hdu6682 一模一样。a 中每个元素只会对应于 b 中两个位置,扫一下即可。

    gym102307D(银牌题)

    题意:给一个字符串,q 次操作每次把 (i+ja (j le k)) 的所有位置改成一个字符。问最终序列。

    key:分块

    按 k 暴力,大于 B 的话那么 a 一定小于 n/B,那么按模 a 分类,是一个区间,在这个区间做扫描线维护时间戳最大值即可。

    gym102307E(x牌题)

    题意:给极坐标系上若干个点,给定半径范围和角度范围,问能覆盖的最大点数。

    key:线段树

    扫描线。

  • 相关阅读:
    遍历及线索化二叉树
    二叉树
    程序的内存布局
    C语言一些易混淆的概念
    C语言标准库函数memcpy和memmove的区别以及内存重叠问题处理
    柔性数组
    一个基于QT简单登录对话框(带验证码功能)
    Qt中的布局管理器
    Qt中的标准对话框
    一个基于QT简单登录对话框
  • 原文地址:https://www.cnblogs.com/dqsssss/p/11198534.html
Copyright © 2011-2022 走看看