2124A - Deranged Deletions

m=2m=2 的降序序列总是 derangement 的。

如果一个序列存在i<ji<jai>aja_{i}>a_{j},即存在m=2m=2 的降序子序列,则保留这两个元素,derangement。

否则,这个序列单调不降,其任意一个子序列也单调不降,不存在 derangement 的子序列。

复杂度O(n2)\mathcal{O}(n^{2}),也可以做到O(n)\mathcal{O}(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
auto solve = [&] {
int n;
cin >> n;

vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (a[i] > a[j]) {
cout << "YES" << endl;
cout << 2 << endl;
cout << a[i] << ' ' << a[j] << endl;
return;
}
}
}
cout << "NO" << endl;
};

2124B - Minimise Sum

为影响最小,选择i+1=ji+1=j(这样只有最多一项的值变大),枚举ii 即可。

复杂度O(n)\mathcal{O}(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
auto solve = [&] {
int n;
cin >> n;

vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

ll res = inf;
ll sum = 0;
int minn = inf;
for (int i = 0; i < n; i++) {
if (i < n - 1) {
res = min(res, sum + min(minn, a[i] + a[i + 1]));
}
if (a[i] < minn) {
minn = a[i];
}
sum += minn;
}
res = min(res, sum);
cout << res << endl;
};

2124C - Subset Multiplication

如果某个bi1bib_{i-1}\nmid b_{i},必须置x:=bi1gcd(bi1,bi)x := \dfrac{b_{i-1}}{\gcd(b_{i-1}, b_{i})},并给bi1b_{i-1} 除掉xx

如果有多个bi1bib_{i-1}\nmid b_{i},就置xx 为所有bi1gcd(bi1,bi)\dfrac{b_{i-1}}{\gcd(b_{i-1}, b_{i})} 的最小公倍数 lcm,这样每个都能满足要求了。

每个数只由它后面的数决定,因此倒着循环。

复杂度O(nlogn)\mathcal{O}(n\log n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
auto solve = [&] {
int n;
cin >> n;

vector<int> b(n);
for (int i = 0; i < n; i++) {
cin >> b[i];
}

int x = 1;
for (int i = n - 1; i > 0; i--) {
if (b[i] % b[i - 1] != 0) {
x = lcm(x, b[i - 1] / gcd(b[i - 1], b[i]));
b[i - 1] /= x;
}
}
cout << x << endl;
};

2124D - Make a Palindrome

需要几个观察:

  1. 数组最后剩下的元素个数k1\geqslant k-1

  2. 比整个数组的第kk 小数ww 还要小的数,一定删不掉。

  3. ww 大的数,一定能删掉。

  4. 在 1 的条件下,等于ww 的数,想删就能删。这是因为执行 3 之后,等于ww 的数始终是整个数组的第kk 小,可以删去任意一个。

现在我们贪心地,

  • 把比ww 大的数全删了。

  • 判断比ww 小的数能否构成回文串,如果不能,NO。

  • 如果数量不足k1k-1,就还需要等于ww 的数来补全,这些等于ww 的数是插空站在小于ww 的数中间的,每个空里等于ww 的数量也要构成回文串,左右对称。

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
auto solve = [&] {
int n, k;
cin >> n >> k;

vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

auto b = a;
sort(b.begin(), b.end());
int w = b[k - 1];

vector<int> c;
int cnt = 0;
vector<int> d;
for (int i = 0; i < n; i++) {
if (a[i] < w) {
c.push_back(a[i]);
d.push_back(cnt); // 每个空里等于 w 的数量
cnt = 0;
} else if (a[i] == w) {
cnt++;
}
}
d.push_back(cnt); // 每个空里等于 w 的数量

for (int i = 0; i < d.size(); i++) {
d[i] = min(d[i], d[d.size() - i - 1]); // 每个空里等于 w 的数量也要构成回文串,左右对称
}

if (c != vector(c.rbegin(), c.rend())) { // 比 w 小的数不回文
cout << "NO" << endl;
return;
}

if (accumulate(d.begin(), d.end(), 0) + c.size() < k - 1) { // 数量还不够
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
};

2124E - Make it Zero

不知道 17 次哪来的,两次足够。

首先判掉一次的情形。

否则设最中间的那个元素为aia_{i}D=j=1i1aj\displaystyle D=\sum_{j=1}^{i-1}a_{j}E=j=i+1naj\displaystyle E=\sum_{j=i+1}^{n}a_{j},满足D+aiE,Dai+E(1)D+a_{i} \geqslant E,\quad D \leqslant a_{i}+E \quad (1)

先试试两次行不行,如果可以,就是拆成

{D1+ai1=E1D2=ai2+E2\begin{cases} D_{1}+a_{i 1}=E_{1} \\ D_{2}=a_{i 2}+E_{2} \end{cases}

解这个方程能得到ai1=E+aiD2,ai2=D+aiE2a_{i 1}=\dfrac{E+a_{i}-D}{2}, \quad a_{i 2}=\dfrac{D+a_{i}-E}{2},这俩东西按 (1) 得知都是非负的,可行。

只需验证是否满足ai1E1E,ai2D2Da_{i 1} \leqslant E_{1} \leqslant E, \quad a_{i 2} \leqslant D_{2} \leqslant D 即可,如不满足一定无解(直观感受是最中间的那个大数特别大,1 100000 2 3 这样的)。

其余情况一定有解,随便构造就行,比如可以取

{0+ai1=ai1D=ai2+(Eai1)\begin{cases} 0+a_{i 1}=a_{i 1} \\ D=a_{i 2}+(E-a_{i 1}) \end{cases}

这样两组。

代码写得有点丑

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
62
63
64
65
66
67
68
69
70
71
72
73
auto solve = [&] {
int n;
cin >> n;

vector<ll> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

ll sum = accumulate(a.begin(), a.end(), 0ll);
if (sum % 2 != 0) {
cout << -1 << endl;
return;
}
ll sum1 = 0;
int i = 0;
while (i < n && sum1 < sum / 2) {
sum1 += a[i];
i++;
}
if (sum1 == sum / 2) {
cout << 1 << endl;
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
cout << endl;
} else {
i--;

ll D = sum1 - a[i];
ll E = sum - sum1;
ll ai1 = (E + a[i] - D) / 2;
ll ai2 = (D + a[i] - E) / 2;

if (E < ai1 || D < ai2) {
cout << -1 << endl;
return;
}

cout << 2 << endl;

ll need = D;
for (int j = 0; j < i; j++) {
ll x = min(need, a[j]);
cout << x << " ";
need -= x;
a[j] -= x;
}
cout << ai2 << " ";

need = E - ai1;
for (int j = i + 1; j < n; j++) {
ll x = min(need, a[j]);
cout << x << " ";
need -= x;
a[j] -= x;
}
cout << endl;

for (int j = 0; j < i; j++) {
cout << 0 << " ";
}
cout << ai1 << " ";

need = ai1;
for (int j = i + 1; j < n; j++) {
ll x = min(need, a[j]);
cout << x << " ";
need -= x;
}
cout << endl;
}
};

2124F1 - Appending Permutations (Easy Version)

很容易想到按每一段来 DP,设fif_{i} 表示只考虑前ii 个数的方案数,从fj, j<if_{j},\ j<i 转移而来,复杂度O(n2)\mathcal{O}(n^{2})

至于转移系数,F1 的数据范围允许我们枚举s,rs,r,看有多少合法。

总复杂度O(n4)\mathcal{O}(n^{4})

这一部分的代码:(还有第二部分)

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
auto solve = [&] {
int n, m;
cin >> n >> m;

vector adj(n + 1, vector<int>(n + 1));
while (m--) {
int i, x;
cin >> i >> x;
adj[i][x] = 1;
}

vector<Z> f(n + 1);
f[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
Z sum = 0;
int s = i - j;
for (int r = 1; r <= s; r++) {
int pos = j;
int num = r - 1;
bool ok = true;
for (int _ = 1; _ <= s; _++) {
pos++;
num %= s;
num++;
if (adj[pos][num]) {
ok = false;
}
}
if (ok) {
f[i] += f[j];
}
}
}
}
cout << f[n] << endl;
};

测样例发现算多了。

比如 1 2 3 4 1 2,从j=2j=2 可以转移到i=6i=6,从j=4j=4 也可以转移到i=6i=6,就算重复了。

计算重复只有可能是,两个段的r=1r=1,且前一个段的ss 大于这一个段的ss

修改 DP 数组为fi,sf_{i,s} 表示只考虑前ii 个数,且最后一个段长为ss 的全部方案数。再引入辅助数组gi,sg_{i,s} 表示只考虑前ii 个数,最后一个段长为ss,且最后一个段的r=1r=1 的方案数。

转移的时候如果r=1r=1,把相应的gg 减掉即可。

总复杂度O(n4)\mathcal{O}(n^{4})

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
auto solve = [&] {
int n, m;
cin >> n >> m;

vector adj(n + 1, vector<int>(n + 1));
while (m--) {
int i, x;
cin >> i >> x;
adj[i][x] = 1;
}

vector f(n + 1, vector<Z>(n + 1));
vector g(n + 1, vector<Z>(n + 1));
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
Z sum = 0;
int s = i - j;
for (int r = 1; r <= s; r++) {
int pos = j;
int num = r - 1;
bool ok = true;
for (int _ = 1; _ <= s; _++) {
pos++;
num %= s;
num++;
if (adj[pos][num]) {
ok = false;
}
}
if (ok) {
for (int k = 0; k <= n; k++) {
f[i][s] += f[j][k];
if (r == 1) {
g[i][s] += f[j][k];
}
}
if (r == 1) {
for (int k = s + 1; k <= n; k++) {
f[i][s] -= g[j][k];
}
}
}
}
}
}
Z res = 0;
for (int i = 1; i <= n; i++) {
res += f[n][i];
}
cout << res << endl;
};