T1 抽象代数题
真·构造题
100pts
-
⾸先容易发现只要做到⼀个⽀持 [ 2 … n ] [2…n] [2…n]循环移位和 ( 1 , 2 ) (1,2) (1,2)交换 的排列,就可以完成整个事情,即每次把待交换的数字放 到 1 1 1号位,然后把另⼀个数字放到 2 2 2号位,交换即可。
-
但这样的时间复杂度是 O ( n 2 ) O(n^2) O(n2)级别的,因为要做很多次循 环移位操作。
-
我们可以构造类似循环左移1位,3位,9位,27位这样的四 个排列,这样之后每次移位就可以期望不到4次快速完成, 假设期望是c次。
-
但是对于交换⼀对数,直接写的话常数会⽐较⼤,⽐如说 将u移到1位置然后换到0位置,将v移到1位置然后和0位置 的u交换,然后u,v互换,再把0换回来。这样步数会是3cn级别,可以获得⼀些部分分。
-
因为后⾯的数⼀直在循环移位,所以我们可以想保持他们 相对位置正确即可,所以我们假设要做的是使得后⾯的数 变成2,3,…, n的⼀个循环。
-
所以对于1号位的数字,我们想做的是把它换到正确的相对 位置。
-
⽐如说1号位是5,那么它正确的相对位置就是2后⾯的3 格,只需要把这个格⼦移到2号位,然后与1交换即可。
-
如果1号位现在是1,并且后⾯仍然不是有序的,那么找到 ⼀个与位置不对的数字,然后将其换到1号位。
-
这样⼦做期望步数是cn级别的,⼤概200次左右就可以完 成。
T2 数据结构题
真·计数题
50pts
-
对于⼀个⼦集 s s s,它的⽅差是 ( 元 素 的 平 ⽅ 和 / ∣ s ∣ ) − ( 元 素 和 的 平 ⽅ ) / ∣ s ∣ 2 (元素的平⽅和/|s|)-(元素和的平 ⽅)/|s|^2 (元素的平⽅和/∣s∣)−(元素和的平⽅)/∣s∣2。
-
通过⼀些常⻅的计数⽅法我们需要对每个元素以及每对元 素分别算贡献。
-
对于⼀个元素 v v v,它前⼀部分是 v 2 ∗ s u m ( C ( n − 1 , k − 1 ) / k ) v^2 * sum(C(n-1,k-1)/k) v2∗sum(C(n−1,k−1)/k)
-
对于⼀对元素 v [ i ] v[i] v[i], v [ j ] v[j] v[j],如果 i = j i=j i=j,那么它对答案的贡献是 v [ i ] ∗ v [ i ] ∗ s u m ( C ( n − 1 , k − 1 ) / k 2 ) v[i] * v[i] * sum(C(n-1,k-1)/k^2) v[i]∗v[i]∗sum(C(n−1,k−1)/k2)
如果 i ≠ j i≠j i̸=j,那么它对答案的贡献是 v [ i ] ∗ v [ j ] ∗ s u m ( C ( n − 2 , k − 2 ) / k 2 ) v[i] * v[j] * sum(C(n-2,k-2)/ k^2) v[i]∗v[j]∗sum(C(n−2,k−2)/k2)
-
所以说答案就是:
数 字 平 ⽅ 和 ∗ ( s u m ( C ( n − 1 , k − 1 ) / k ) − s u m ( C ( n − 1 , k − 1 ) / k 2 ) + s u m ( C ( n − 2 , k − 2 ) / k 2 ) ) − 数 字 和 的 平 ⽅ ∗ s u m ( C ( n − 2 , k − 2 ) / k 2 ) 1 数字平⽅和 * (sum(C(n-1,k-1)/k)- sum(C(n-1,k-1)/k^2)+sum(C(n-2,k-2)/k^2))-数字和的平⽅ *sum(C(n-2,k-2)/k^2) 1 数字平⽅和∗(sum(C(n−1,k−1)/k)−sum(C(n−1,k−1)/k2)+sum(C(n−2,k−2)/k2))−数字和的平⽅∗sum(C(n−2,k−2)/k2)1
100pts
-
数字和的平⽅与数字平⽅和可以⽤线段树简单维护。
-
怎么算?
-
$ C[n]=sum(C(n-1,k-1)/k)-sum(C(n-1,k-1)/k2)+sum(C(n-2,k-2)/k2))$
-
B [ n ] = s u m ( C ( n − 2 , k − 2 ) / k 2 ) B[n]=sum(C(n-2,k-2)/k^2) B[n]=sum(C(n−2,k−2)/k2)
-
这是两个只与n有关的序列:
s u m ( C ( n − 1 , k − 1 ) / k ) = s u m ( n − 1 ) ! / ( n − k ) ! / k ! = 1 / n sum(C(n-1,k-1)/k)=sum (n-1)!/(n-k)!/k!=1/n sum(C(n−1,k−1)/k)=sum(n−1)!/(n−k)!/k!=1/n s u m C ( n , k ) = ( 2 n − 1 ) / n sum C(n,k)=(2^n-1)/n sumC(n,k)=(2n−1)/n
-
A [ n ] = s u m ( C ( n − 1 , k − 1 ) / k 2 − C ( n − 2 , k − 2 ) / k 2 ) ) = s u m ( C ( n − 2 , k − 1 ) / k 2 ) A[n]=sum(C(n-1,k-1)/k^2-C(n-2,k-2)/k^2))=sum(C(n-2,k-1)/k^2) A[n]=sum(C(n−1,k−1)/k2−C(n−2,k−2)/k2))=sum(C(n−2,k−1)/k2)
-
则 A [ n ] = 1 / ( n − 1 ) ∗ s u m C ( n − 1 , k ) / k A[n]=1/(n-1) *sum C(n-1,k)/k A[n]=1/(n−1)∗sumC(n−1,k)/k,考虑. n A [ n + 1 ] − ( n − 1 ) A [ n ] = s u m C ( n − 1 , k − 1 ) / k nA[n+1]-(n-1)A[n]=sum C(n-1,k-1)/k nA[n+1]−(n−1)A[n]=sumC(n−1,k−1)/k
-
s u m C ( n − 1 , k − 1 ) / k 2 sum C(n-1,k-1)/k^2 sumC(n−1,k−1)/k2可以⽤相同的⽅法推,所以A,B可以在 线性时间内求出
T3 组合计数题
50pts
- 将所有区间按照右端点排序,依次考虑。
- 实际上我们要记的是当前没有被选中且没有被覆盖的区间 的最⼩的右端点和选中的区间最⼤的右端点。
- 每次考虑⼀个区间,如果选进来,就更新选中的区间最⼤ 的右端点,并且如果能覆盖没有被选中的最⼩右端点也可 以更新。
- 否则就含是否被最⼤的右端点覆盖,如果有就覆盖,否则 更新最⼩右端点。
100pts
- 这样的状态设计是 O ( n 3 ) O(n^3) O(n3)的不太好进⼀步分析,因此 令 l ( i ) l(i) l(i)表示 i i i前⾯最⼤的不能被i⽀配的点的下标, r ( i ) r(i) r(i)表示 i i i后⾯ 的最⼩的。
- 虽然每个点可能会覆盖某个⼦集,但是我们只需要考虑 l ( i ) + 1 , … , r ( i ) − 1 {l(i)+1,…, r(i)-1} l(i)+1,…,r(i)−1这个区间即可。
- 对于更右边的点,如果能被 i i i覆盖,那么也⼀定能被覆盖 r ( i ) r(i) r(i) 的点覆盖。问题就变成有⼀堆线段,选⼀个⼦集使得并是全集。
- 考虑 d p [ i ] [ j ] dp[i][j] dp[i][j]表示考虑前 i i i个区间,最后⼀个没有被覆盖的位置 是 j j j。
- 那么从 d p [ i ] dp[i] dp[i]转移到 d p [ i + 1 ] dp[i+1] dp[i+1]就是⼀个前缀*2,然后区间加。
- 所以可以⽤线段树解决。
今日总结
1.上午改了之前没过的几道题的正解,看大佬的题解,理解思路后自己写代码,收获还是很大的。
2.下午把上午模拟赛的题写了,实在不会打的就听讲解,跟着老师的思路写题。有些题完全想不到正解那个方向,只会很暴力的暴搜,实在是很无奈。不过每天都是有收获吧。
3.写了篇FFT的博客写了一天(昨天学了一晚上终于学会了orz)。写博客还是有好处的,至少之前可能一知半解的内容发现了不足后找各种资料学习,基本上都理解了。