模拟赛8.18 解题报告
T1. 动态数点(JZOJ6294)
题意:求最大的 \(r-l\) 满足 \(1\le l\le r\le n\),且存在一个 \(k(l\le k\le r)\),使得 \(\forall i,a_k|a_i\)。并输出有哪些满足条件的 \(l,r\)。\(1\le n\le5\times10^5\)
题目相当于找一个区间,满足存在一个数是区间所有数的因数。
那么这样的数必定是区间的最小值,于是条件转化为 \(\min\limits_{i=l}^r \{a_i\}=\gcd\limits_{i=l}^r\{a_i\}\)。
不难想到区间长度具有可二分性,考虑二分区间长度。然后用 \(\text{ST}\) 表查询区间最小值、区间 \(\gcd\)。理论复杂度 \(O(n\log^2n)\),但远远达不到这个上界。
T2. 中位数
题意:给出 \(n\) 个数 \(a_{1..n}\),求所有非空子集的和的中位数。\(1\le n,a_i\le2000\)
猜测中位数也许和平均数有关,设平均值 \(v=\frac{\sum_S\sum_{i\in S}a_i}{2^n}=\frac{\sum_{i=1}^na_i}2\)。对于一个子集 \(S\),设和为 \(\text{sum}(S)\),取补集 \(S'=\{1,2,...,n\}-S\),那么有 \(\text{sum}(S)+\text{sum}(S')=\sum\limits_{i=1}^na_i\) 即 \(\frac{\text{sum}(S)+\text{sum}(S')}2=v\)。
把 \(\text{sum}(S),\text{sum}(S'),v\) 三个数放在数轴上理解(钦定 \(\text{sum}(S)<\text{sum}(S')\)):
那么 \(\text{sum}(S)\) 与 \(\text{sum}(S')\) 在数轴上关于 \(v\) 对称。
我们给集合分类:\(\text{A}\) 类和 \(\text{B}\) 类。对于 \(\text{A}\) 类,每个数 \(\le v\);对于 \(\text{B}\) 类,每个数 \(\ge v\)。
进一步可得,对于两个互为补集的集合 \(S,S'\),若 \(\text{sum}(S)<\text{sum}(S')\),那么把 \(S\) 分到 \(\text{A}\) 类,把 \(S'\) 分到 \(\text{B}\) 类,反过来类似。
一共有 \(2^{n-1}\) 对互为补集的子集,那么此时 \(\text{A,B}\) 类子集都各有 \(2^{n-1}\) 个。
因为不算空集,我们要在 \(\text{A}\) 类里面把空集去掉,此时 \(\text{A}\) 有 \(2^{n-1}-1\) 个、\(\text{B}\) 有 \(2^{n-1}\) 个。注意到中位数其实就是求第 \(2^{n-1}\) 小的数,于是答案就是 \(\text{B}\) 类中和最小的子集的 \(\text{sum}\)。由于 \(\text{A}\) 类中 \(=v\) 的数并不影响答案,我们相当于是求 \(\ge v\) 的和最小的子集。
如何求和 \(\ge v\) 且最小的子集?直接背包。这是一个 \(01\) 可行性背包,用 \(\text{bitset}\) 优化即可。时间复杂度 \(O(\frac{n^2a}w)\)。
T3. 出题
题意:有 \(n\) 个数 \(a_{1...n}\) 和 \(q\) 个操作,每个操作有两种可能:
-
\(\text{1 l r d}\):令 \(a_l,a_{l+1},...,a_r\) 都加上 \(d\)。
-
\(\text{2 l r}\):查询 \(\sum\limits_{i=l}^r a_i\)
一开始 \(a_1=a_2=...=a_n=0\)。给出 \(P_{1...q-1}\),依次删去第 \(P_1,P_2,...,P_{q-1}\) 次操作,求出一开始和每删一个操作后的查询出的答案的总和。\(1\le n,q\le 2\times10^5,\space 1\le P_i\le q\) 且 \(P_i\) 两两不同。
首先考虑如何只用树状数组求解。若没有删除,那么这是个区间加、区间查询的板子,可以用两个树状数组解决。
考虑每次删除一个操作,若删除的是区间加操作,那么这会与之后的查询产生贡献,对总答案产生贡献;若删除的是区间查询操作,这会与之前的加操作产生贡献。
这就有点三位偏序的感觉了,但思路还是不够清晰。
我们把每一个操作都放到对应的删除时间上,最后一个没删除的操作对应结尾时间。设 \(t_i\) 表示时刻 \(i\) 删除的操作在原操作序列的顺序(第几个),那么若 \(i,j(i<j)\) 产生了贡献,我们在位置 \(i\) 上记录贡献,最后前缀和一遍即可。
于是,删除时间一维,原操作序列的顺序一维,以及区间操作一维,直接 \(\text{cdq}\) 即可。
T4. 排列(AGC005D ~K Perm Counting)
题意:求有多少个 \(1...n\) 的排列 \(A_1,A_2,...,A_n\),满足 \(\forall i,|A_i-i|\not =k\)。\(1\le n,k\le1000\)
万重原题。
考虑若没有绝对值符号,即 \(\forall i,A-i-i\not =k\),所有的 \(i\) 独立,直接容斥就行了。
但是现在 \(A_i\) 不能取两个值:\(i-k,i+k\)。思路还是容斥,考虑建立二分图,左边表示点编号,右边表示取值。把 \(i\) 与 \(i+k,i-k\) 连边,选择一条边相当于一个不合法的匹配。
发现建出来的图是由 \(2k\) 条链组成,显然每条链互相独立。设 \(f[i,j,0/1]\) 表示 \(i\) 个点的链,选了 \(j\) 条边,最后一条边是/否选择的方案数。
由于选边表示匹配,那么选择的不同的两条边不能连接一个相同的点。转移:
对于 \(2k\) 条链,考虑背包合并。设 \(g[i,j]\) 表示前 \(i\) 条链选择 \(j\) 条边的方案数,转移略。
最后的答案即为
时间复杂度 \(O(nk\cdot \frac nk)=O(n^2)\)。