JOISC2022 Day2 题解

First Post:

Last Update:

事实上没有通信题题解。

这两题都没想出完整做法,不知道是不是睡眠质量的问题。按道理来说都应是 “刚好会做” 的题。

按体感难度排序。

loj3690 团队竞技

题意: https://loj.ac/p/3690

老老实实地想了除最后一个包的所有点。

无语住了。

按 从小到大排序,扫描 最大的那个人,剩下两人都要在他前面。设剩下两人为 ,那么有 。

我们加入一个 的时候,其实就可以找到与其配对的另一个人 (满足 的 中 最大的,以及满足 的 中 最大的)。

将 与和其配对的点合成一对 ,扔到一个二维数据结构里面。扫描到 时,查询 的 最大值即可。

时间复杂度 。

loj3688 复制粘贴 3

题意 : https://loj.ac/p/3688

想到了区间 DP,但脑子不在状态,只是描绘了一个复制粘贴的形态,没有在纸上细推,导致转移没有编出来。

至于如何想到,其实可以考虑观察样例 的构造过程。其没有明显的顺序或倒序构造的特征,不过仍然是从小串构造出包含它的大串,即 ,这反映到区间上就是 。一条区间 DP 的转移路径。

那么我们可设 表示不管当前的剪贴板是什么,构造出 的最小步数。

考虑我们剪切了一个串后,用其构造出来的串一定形如 (其中 都是我们手打的字符)。

其中头尾两段不是很好处理,我们考虑编两个转移来包含这两段。

显然有 (其中 表示 )。

考虑 怎么办 ,我们设 表示构造出 ,且最后一次操作是粘贴的最小步数。

当我们从 向外转移时,可以先令 ,然后 。

依据 的定义,我们可以转移 。

至此, 和 都处理完了。

那么我们多次粘贴 时,可以默认我们构造出来的串开头是 ,结尾也是 。

那么我们设 表示 最早的出现位置使得该位置不在 中。

那么我们每次转移暴力跳 即可转移。

因为我们至多跳 次,复杂度是 的 ( 是调和级数)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <bits/stdc++.h>
using namespace std;
template<typename T> inline void ckmin(T &x,const T &y) { if(x > y) x = y;}
const int N = 2.5e3 + 5;
typedef unsigned long long ull;
const ull base = 13331;
int n;
int A,B,C;
char s[N];
int nxt[N][N];
long long f[N][N],g[N][N];
ull hsh[N],Pow[N];
ull h[N][N];
map<ull,int> ton;
int main()
{
cin >> n;
cin >> (s + 1);
cin >> A >> B >> C;
Pow[0] = 1;
for(int i = 1;i <= n;i++) Pow[i] = Pow[i - 1] * base;
for(int i = 1;i <= n;i++) hsh[i] = hsh[i - 1] * base + (s[i] - 'a' + 1);
for(int i = 1;i <= n;i++)
for(int j = i;j <= n;j++)
h[i][j] = hsh[j] - hsh[i - 1] * Pow[j - i + 1];
for(int l = 1;l <= n;l++)
{
ton.clear();
for(int i = n - l + 1;i + l - 1 > n - l;i--)
nxt[i][i + l - 1] = n + 1;
for(int i = n - l - l + 1;i >= 1;i--)
{
ton[h[i + l][i + l + l - 1]] = i + l;
if(ton.find(h[i][i + l - 1]) == ton.end())
nxt[i][i + l - 1] = n + 1;
else nxt[i][i + l - 1] = ton[h[i][i + l - 1]];
}
}
memset(f,0x3f,sizeof f);
memset(g,0x3f,sizeof g);
for(int i = 1;i <= n;i++)
f[i][i] = A;
for(int len = 1;len <= n;len++)
{
for(int i = 1;i + len - 1 <= n;i++)
{
int j = i + len - 1;
ckmin(f[i][j],g[i][j]);
if(j < n) ckmin(f[i][j + 1],f[i][j] + A);
int now = nxt[i][j],cnt = 2;
while(now <= n)
ckmin(f[i][now + len - 1],f[i][j] + B + 1ll * cnt * C + 1ll * A * (now + len - i - cnt * len)),
++cnt,now = nxt[now][now + len - 1];
}
for(int i = 2;i + len - 1 <= n;i++)
ckmin(f[i - 1][i + len - 1],f[i][i + len - 1] + A);
}

cout << f[1][n] << endl;
return 0;
}