2024.8.22
T1 Skip
\(dp(i)=\max_{j=1}^{i-1}(dp(j)+a_i-\frac{(i-j-1)(i-j)}2)\)
\(\\dp(j)+a_i-\frac{(i-j-1)(i-j)}2> dp(k)+a_i-\frac{(i-k-1)(i-k)}2\\ \frac{(dp(j)-\frac{j^2}2-\frac{j}2)-(dp(k)-\frac{k^2}2-\frac{k}2)}{j-k}<-i\\F(x)=dp(x)-\frac{x^2}2-\frac{x}2\\\frac{F(j)-F(k)}{j-k}<-i\)
可以证明:若存在\(i<j<k\),满足\(\frac{F(i)-F(j)}{i-j}<\frac{F(j)-F(k)}{j-k}\),那么\(j\)一定不是最优的。(具体证明可以看斜率优化课件)因此,我们要用单调栈维护一个凸包,满足栈中从栈底到头斜率单调递减。也就是下面这样。
显然,凸包中间有一个点,在这个点左边斜率统一大于\(-i\),右边统一小于\(-i\)。而如果斜率大于\(-i\)则右边的比左边的更优,小于\(-i\),则左边的右边的更优。所以中间这个点就是最优点。这个点显然可以二分查找。随着\(i\)的增大,\(-i\)减小,这个点会向左走,这就是不能用单调队列的原因。
考虑dp方程为\(dp(i)=dp(j)+a_i,a_j\le a_i\)时的优化方式(就是带权 LIS)。这时,我们可以开一颗权值线段树,设节点\(u\)代表的区间为\([l,r]\)。那么它记录的是所有\(j\)满足\(l\le a_j\le r\)中,\(dp(j)\)的最大值。放到这道题里,显然不能这么简单的维护,但是可以想到,如果在这个节点上维护一个所有\(j\)满足\(l\le a_j\le r\)组成的凸包,查询时直接在线段树需要的节点上二分查询,再把每个节点二分的最优转移点比较出最优的,问题就解决了。
T2 String
80pts
如果你不打这个部分分可能较难想到正解。
设 \(cnt_i\) 表示第 \(i\) 个字符出现的次数
由于 \(k\le 8\),可以用一个十六进制数表示现在每个 \(cnt_i\) 出现的次数,最终状态就是 \(16^0+16^1+16^2+...\),然后考虑一个记搜,使用 \(reflect_{last,S}\) 表示上一次选的 \(cnt_i\) 是 \(last\),当前 \(cnt\) 选取状态为 \(S\) 时的串的排位。
对于第 \(u\) 位的放置,可以用现有的 \(reflect\) 更新当前的 \(reflect\),具体地,对每次放完所有元素但是不确定排名的 \(last,endS\) 将其映射排名设定为 1,搜索过程中的每个映射就可以通过累积枚举放置元素得到的排位确定当前 \(last,S\) 的排位。实现是较容易的。
优化
在放置元素的过程中每个 \(cnt_i\) 出现的次数是难以判断取舍的,比如同时出现了多个 \(cnt_1\) 时你不能认为这些 \(cnt_1\) 一定不会在之后叠加成其他的状态,进而无法在当前情况下做出状态合法性的取舍,不过容易发现 \(cnt_i\) 的转移是递增的,即对于每个 \(cnt_i\),它在任何合法的不完全状态中出现的次数不可能超过 \(k-cnt_i+1\),否则会导致最终状态中 \(cnt_j,j<i\) 的缺失。
100pts
观察样例发现合法的串数量随 \(k\) 的增加增长地极快,加上确实难以再减枝的记搜,猜测 \(10^{18}\) 可能难以超出甚至远低于较大的 \(k\) 的答案集合,用80pts的记搜跑 \(k=7,n=10^{18}\) 的点发现答案是 ababababenamoyemobeoenbnebna
,不出意外于答案前部出现大量ab状物,于是提出在 \(k\ge 7\) 时先交替输出 \(k,k-1,k-2,k-3...\) 次 \(a,b,c,d...\),直接拿记搜的代码把条件换成 \(k\ge 8\),对所有大于 8 的 k 先输出并减去直到 k 落到 8 以内,记录此时输出到的字符,记搜的时候从那个位置开始枚举即可。
T3 Permutation
不断递增到 \(n+m-k\)。接着前面的数字将先变为 \(111011\),这样第 \(m\) 位的初始值为 \(m+1\),然后会递增到 \(n+m-k\),同理因为最后一位数字向后移动,所以初始值递增到 \(n+m-k\),然后倒数第二位数字也向后移动,导致初始值变为 \(m+2\),同理上面的过程,这个初始值也会不断递增到 \(n+m-k\)。接着前面的数字变为 \(110111\),然后类似的重复上面的过程。
于是我们不妨设 \(f_{i,j}\) 表示移动了 \(i\) 个第 \(m\) 位前面的数字,第 \(m\) 位的初始活动范围大小为 \(j\) 的答案。那么 \(f_{i,j}\) 可以理解为先移动 \(i-1\) 个数字完成所有排列,接着将第 \(i\) 个数字后移一位,缩短第 \(m\) 位的活动范围。不难发现两个步骤之间,第 \(m\) 位的活动范围从 \([n+m-k-j,n+m-k]\) 变为了 \([n+m-k-j+1,n+m-k]\),也就是第 \(m\) 位,从 \(n+m-k\) 移动到了 \(n+m-k-j+1\),相差为 \(j-1\),那么有转移方程
\(f_{i,j}=f_{i-1,j}+f_{i,j-1}+(j-1)\)
那么我们最终要求的就是完全移动 \(m-1\) 个数字,第 \(m\) 位的初始活动范围为 \([m,n+m-k]\),大小为 \(n-k\) 时的答案,即 \(f_{m-1,n-k}\)。边界条件为 \(f_{0,i}=i\)。那么此时的复杂度是 \(O(n^2)\) 的,可以拿到 \(70\) pts。
那么我们在此给出部分 \(f\) 数组,行l列的编号从 \(0\) 开始
\(\begin{matrix} 0&1&2&3&4&5&\cdots\\ 0&1&4&9&16&25&\cdots\\ 0&1&6&17&36&65&\cdots\\ 0&1&8&27&66&135&\cdots\\ 0&1&10&39&108&247&\cdots\\ 0&1&12&53&164&415&\cdots\\ \vdots&\vdots&\vdots&\vdots&\vdots&\vdots&\ddots \end{matrix}\)
那么我们发现除了前两行和前三列以外,似乎该数组毫无规律(如果你能聪明到把每一行都加上他对应第几列然后除以二那我无话可说),那么我们考虑到因为只需要求解最后一行的一个数,那么我们不妨对每一行进行差分,于是得到了数组 \(g_1\)(因为第一列全是 \(0\) 所以忽略了):
\(\begin{matrix} 1&1&1&1&1&\cdots\\ 1&3&5&7&9&\cdots\\ 1&5&11&19&29&\cdots\\ 1&7&19&39&69&\cdots\\ 1&9&29&69&139&\cdots\\ \vdots&\vdots&\vdots&\vdots&\vdots&\ddots \end{matrix}\)
我们貌似已经发现了一些规律,比如这个数组关于主对角线对称,但是似乎没有什么用,因为关于每一行来看依旧毫无头绪。考虑到我们甚至不需要直到一行所有的数,而只需要知道一个点,那么我们考虑二维差分前缀和,于是得到了数组 \(g_2\):
\(\begin{matrix} 1&1&1&1&1&\cdots\\ 0&2&4&6&8&\cdots\\ 0&2&6&12&20&\cdots\\ 0&2&8&20&40&\cdots\\ 0&2&10&30&70&\cdots\\ \vdots&\vdots&\vdots&\vdots&\vdots&\ddots \end{matrix}\)
到了这里,我们惊奇的发现,这个数组似乎有一些规律,更具体的我们忽略第一行和第一列,并补齐另一个不一样的行,那么我们得到了:
\(\begin{matrix} 2&2&2&2&\cdots\\ 2&4&6&8&\cdots\\ 2&6&12&20&\cdots\\ 2&8&20&40&\cdots\\ 2&10&30&70&\cdots\\ \vdots&\vdots&\vdots&\vdots&\ddots \end{matrix}\)
不知道大家有没有对这个矩阵感到熟悉,这个矩阵是一个经典小学奥数题答案的矩阵乘二的结果:从 \((1,1)\) 出发,只能朝下或者朝右走,问走到 \((n,m)\) 的方案数,我们可以将这个矩阵改写成
\(\begin{matrix} 2C_0^0&2C_{1}^{0}&2C_{2}^{0}&2C_{3}^0&\cdots\\ 2C_1^1&2C_2^1&2C_3^1&2C_4^1&\cdots\\2C_2^2&2C_3^2&2C_4^2&2C_5^2&\cdots\\ 2C_3^3&2C_4^3&2C_5^3&2C_6^3&\cdots\\ 2C_4^4&2C_5^4&2C_6^4&2C_7^4&\cdots\\ \vdots&\vdots&\vdots&\vdots&\ddots \end{matrix}\)
而我们要求的是对 \(g_2\) \([0,m-1]\) 行和 \([0,n-k]\) 列的求和,那么我们需要这个矩阵 \([1,m]\) 行和 \([1,n-k-1]\) 列的求和。而对这样的矩阵求和只可谓是传统艺能,我们能够轻易的求得答案为 \(2(C_{m+n-k-1}^{m}-1)\),但是因为第一行是额外添加的,因此我们需要将原来的第一行加上,同时将第一行减去,所以答案为 \(2(C_{m+n-k-1}^{m}-1)-2(n-k-1)+(n-k)=2C_{m+n-k-1}^{m}-(n-k)\),所以我们预处理组合数之后,输出
\(2C_{m+n-k-1}^{m}-(n-k)\)
就可以拿到 \(O(n)\) 预处理 \(O(1)\) 查询拿到满分的成绩。
T4 小P的生成树
考虑枚举所有答案时的 \(\theta\),定义一条边的贡献就是它的三角表示于这个方向上的射影,认为其他方向上的贡献会被抵消。
- 为什么可以认为其他方向的值会被抵消?
其实不能这么认为,但本题是一道最优化策略类题目,对于任何其他方向不被抵消的选取方案,容易证明存在一个与之配对的 \(\theta'\) 使得其余方向的值会被抵消,且此时的贡献一定更大。
之后对于每个枚举的 \(\theta\) 计算出贡献从大到小排序建最大生成树,对所有 \(\theta\) 的答案取极大值即可。
for(double d{};d<=2*pie;d+=0.5)
{
for(int i{1};i<=n;i++) f[i] = i;
int cnt{};
for(int i{1};i<=m;i++) e[i].len = (double)e[i].w1*cos(d)+(double)e[i].w2*sin(d);
sort(e+1,e+1+m,[](EDGE a,EDGE b)
{
return a.len < b.len;
});
double ans1{},ans2{};
for(int i{1};i<=m;i++)
{
int x = e[i].from,y = e[i].to;
int fx = find(x),fy = find(y);
if(find(x) != find(y))
{
f[fx] = fy;
ans1 += e[i].w1;
ans2 += e[i].w2;
cnt++;
}
if(cnt == n-1) break;
}
bestans = max(bestans,sqrt(ans1 * ans1 + ans2 * ans2));
}