Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5cf7709

Browse files
committed
灵茶の试炼 * 50 (无UT)
1 parent 4e09d85 commit 5cf7709

File tree

50 files changed

+5489
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+5489
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package p17;
2+
3+
import java.util.Arrays;
4+
import java.util.Scanner;
5+
6+
public class CF17E {
7+
static int n;
8+
static String s;
9+
10+
public static void main(String[] args) {
11+
Scanner scanner = new Scanner(System.in);
12+
n = scanner.nextInt();
13+
s = scanner.next();
14+
System.out.println(solve());
15+
}
16+
17+
static final int MOD = 51123987;
18+
19+
private static String solve() {
20+
long tot = 0;
21+
22+
StringBuilder t = new StringBuilder("^");
23+
for (char c : s.toCharArray()) t.append("#").append(c);
24+
t.append("#$");
25+
int[] halfLen = new int[t.length() - 2];
26+
halfLen[1] = 1;
27+
int boxM = 0, boxR = 0;
28+
for (int i = 2; i < halfLen.length; i++) {
29+
int hl = 1;
30+
if (i < boxR) hl = Math.min(halfLen[boxM * 2 - i], boxR - i);
31+
while (t.charAt(i - hl) == t.charAt(i + hl)) {
32+
hl++;
33+
boxM = i;
34+
boxR = i + hl;
35+
}
36+
halfLen[i] = hl;
37+
38+
tot += hl / 2;
39+
}
40+
tot %= MOD;
41+
long ans = tot * (tot - 1) / 2;
42+
43+
long[] diff = new long[n + 1];
44+
for (int i = 0; i < halfLen.length; i++) {
45+
int hl = halfLen[i];
46+
int l = (i - hl) / 2, r = (i + hl) / 2 - 2;
47+
if (l <= r) {
48+
diff[(l + r + 1) / 2]++;
49+
diff[r + 1]--;
50+
}
51+
}
52+
// cntEnd := diff[:n]
53+
long[] cntEnd = Arrays.copyOfRange(diff, 0, n);
54+
for (int i = 1; i < n; i++) {
55+
cntEnd[i] += cntEnd[i - 1];
56+
}
57+
for (int i = 1; i < n; i++) {
58+
cntEnd[i] += cntEnd[i - 1];
59+
}
60+
61+
long[] sum = new long[n + 1];
62+
for (int i = 0; i < cntEnd.length; i++) {
63+
sum[i + 1] = (sum[i] + cntEnd[i]) % MOD;
64+
}
65+
for (int i = 0; i < halfLen.length; i++) {
66+
int hl = halfLen[i];
67+
int l = (i - hl) / 2, r = (i + hl) / 2 - 2;
68+
if (l <= r) {
69+
ans -= sum[(l + r) / 2] - sum[Math.max(l - 1, 0)];
70+
}
71+
}
72+
73+
ans = (ans % MOD + MOD) % MOD;
74+
return String.valueOf(ans);
75+
}
76+
}
77+
/*
78+
E. Palisection
79+
https://codeforces.com/contest/17/problem/E
80+
81+
灵茶の试炼 2025年03月07日
82+
题目大意:
83+
输入 n(1≤n≤2e6) 和长度为 n 的字符串 s,只包含小写英文字母。
84+
从 s 中选两个重叠(有公共部分)的非空回文子串,有多少种选法?
85+
答案模 51123987。
86+
87+
rating 2900
88+
正难则反,用所有回文子串对数,减去不重叠的回文子串对数,即为重叠的回文子串对数。
89+
如何计算回文子串的个数?见 力扣 647. 回文子串 (Manacher 算法)
90+
通过 Manacher 算法,可以知道在某个回文中心的最长回文子串的下标区间 [l,r]。剥洋葱,[l+1,r-1],[l+2,r-2],... 这些也是回文子串。
91+
统计以 i 结尾的回文子串个数。对于上述 [l,r] 及其内部的回文子串,右端点最小是 (l+r+1)/2,最大是 r。
92+
所以右端点在 [(l+r+1)/2,r] 中的回文子串个数都要加一。这可以用差分数组维护。
93+
进一步地,计算前缀和,求出右端点 <= i 的回文子串个数 cntEnd[i]。
94+
然后枚举右维护左。
95+
枚举回文中心,设最长回文子串为 [l,r],那么与之不相交的回文子串就是右端点 <= l-1 的子串个数 cntEnd[l-1]。
96+
剥洋葱,对于 [l+1,r-1],[l+2,r-2],... 这些,累加相应的 cntEnd。预处理 cntEnd 的前缀和,便可以快速累加。
97+
注意取模。
98+
注意保证取模结果非负。
99+
代码 https://codeforces.com/problemset/submission/17/309142491
100+
代码备份(洛谷)
101+
======
102+
103+
Input
104+
4
105+
babb
106+
Output
107+
6
108+
109+
Input
110+
2
111+
aa
112+
Output
113+
2
114+
*/
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package p30;
2+
3+
import java.util.Arrays;
4+
import java.util.Scanner;
5+
6+
public class CF30E {
7+
static String s;
8+
9+
public static void main(String[] args) {
10+
Scanner scanner = new Scanner(System.in);
11+
s = scanner.next();
12+
System.out.println(solve());
13+
}
14+
15+
private static String solve() {
16+
int n = s.length();
17+
18+
int[] pi = prefix_function((new StringBuilder(s).reverse() + "#" + s).toCharArray());
19+
pi = Arrays.copyOfRange(pi, n, pi.length); // 注意 pi[0] 对应 '#'
20+
int[] preMaxI = new int[n + 1];
21+
for (int i = 1; i <= n; i++) {
22+
int j = preMaxI[i - 1];
23+
if (pi[i] > pi[j]) {
24+
preMaxI[i] = i;
25+
} else {
26+
preMaxI[i] = j;
27+
}
28+
}
29+
30+
int maxSum = 0, maxI = 0;
31+
int[] halfLen = new int[n];
32+
int boxM = 0, boxR = 0;
33+
for (int i = 0; i < halfLen.length; i++) {
34+
int hl = 1;
35+
if (i < boxR) hl = Math.min(halfLen[boxM * 2 - i], boxR - i);
36+
while (i >= hl && i + hl < n && s.charAt(i - hl) == s.charAt(i + hl)) {
37+
hl++;
38+
boxM = i;
39+
boxR = i + hl;
40+
}
41+
halfLen[i] = hl;
42+
43+
int l = i - hl + 1, r = i + hl - 1;
44+
int k = Math.min(pi[preMaxI[l]], n - 1 - r);
45+
int sum = r - l + 1 + k * 2;
46+
if (sum > maxSum) {
47+
maxSum = sum;
48+
maxI = i;
49+
}
50+
}
51+
52+
int l = maxI - halfLen[maxI] + 1, r = maxI + halfLen[maxI] - 1;
53+
int k = Math.min(pi[preMaxI[l]], n - 1 - r);
54+
if (k == 0) {
55+
return "1\n" +
56+
(l + 1) + " " + (r - l + 1);
57+
} else {
58+
return "3\n" +
59+
(preMaxI[l] - k + 1) + " " + k + "\n" +
60+
(l + 1) + " " + (r - l + 1) + "\n" +
61+
(n - k + 1) + " " + k;
62+
}
63+
}
64+
65+
static int[] prefix_function(char[] s) {
66+
int n = s.length;
67+
int[] pi = new int[n];
68+
for (int i = 1; i < n; i++) {
69+
int j = pi[i - 1];
70+
while (j > 0 && s[i] != s[j]) j = pi[j - 1];
71+
if (s[i] == s[j]) j++;
72+
pi[i] = j;
73+
}
74+
return pi;
75+
}
76+
}
77+
/*
78+
E. Tricky and Clever Password
79+
https://codeforces.com/contest/30/problem/E
80+
81+
灵茶の试炼 2025年03月28日
82+
题目大意:
83+
输入长度 ≤1e5 的字符串 s,只包含小写英文字母。
84+
有一个奇回文串 t,设 t = pre + mid + suf,其中 mid 的长度是奇数,pre 和 suf 的长度相等(可以为 0)。
85+
已知 s = A + pre + B + mid + C + suf(A B C 可以为空),求 t 的最大长度。
86+
输出格式:
87+
首先输出 t 有几段。如果 pre 的长度为 0,则输出 1,否则输出 3。
88+
然后输出每段首字母在 s 中的下标(从 1 开始),以及这一段的长度。
89+
多解输出任意解。
90+
91+
rating 2800
92+
本文讨论的下标,从 0 开始。
93+
枚举 mid 的回文中心 i。
94+
首先证明,在固定回文中心 i 的前提下,mid 越长越好。
95+
如果 mid 不是以 i 为中心的最长回文子串,那么把 mid 向左向右各扩展一个字母,pre 和 suf 的长度至多减一(mid 和 pre suf 贴在一起的情况才会减一),所以总长度不会减少。所以在固定回文中心 i 的前提下,mid 越长越好。
96+
由于只需要计算每个回文中心的最长回文子串,所以用 Manacher 算法。
97+
现在问题变成:已知 mid,求 mid 左右两侧的最长 pre 和 suf。
98+
这可以用 KMP + 前缀最大值解决。
99+
为了匹配 pre 和 suf,计算 rev(s) + '#' + s 的 pi 数组,然后去掉 rev(s),也就是更新 pi 为 pi[n:]。
100+
现在 pi[i+1] 表示以 s[i] 结尾的最长 pre 长度。
101+
由于 pi 数组不是有序的,我们需要计算 pi 数组的前缀最大值数组 preMax。计算完后,preMax[mid 左端点] 就是 pre 的最大长度了。
102+
这个过程中记录 pre + mid + suf 的最大长度及其位置,方便输出。
103+
前缀最大值也可以改成在 pi 数组中的下标,方便输出。
104+
代码(Manacher + KMP) https://codeforces.com/contest/30/submission/311602074
105+
代码备份(洛谷)
106+
注:也可以用 Z 函数做,但需要用单调队列优化。
107+
代码(Manacher + Z 函数) https://codeforces.com/contest/30/submission/311198128
108+
======
109+
110+
Input
111+
abacaba
112+
Output
113+
1
114+
1 7
115+
116+
Input
117+
axbya
118+
Output
119+
3
120+
1 1
121+
2 1
122+
5 1
123+
124+
Input
125+
xabyczba
126+
Output
127+
3
128+
2 2
129+
4 1
130+
7 2
131+
*/
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package p137;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.Scanner;
8+
9+
public class CF137D {
10+
static char[] s;
11+
static int k;
12+
13+
public static void main(String[] args) {
14+
Scanner scanner = new Scanner(System.in);
15+
s = scanner.next().toCharArray();
16+
k = scanner.nextInt();
17+
System.out.println(solve());
18+
}
19+
20+
private static String solve() {
21+
int n = s.length;
22+
int[][] mc = new int[n][n];
23+
for (int i = n - 1; i >= 0; i--) {
24+
for (int j = i + 1; j < n; j++) {
25+
mc[i][j] = mc[i + 1][j - 1];
26+
if (s[i] != s[j]) {
27+
mc[i][j]++;
28+
}
29+
}
30+
}
31+
32+
int[][] fr = new int[k][n];
33+
int[] f = mc[0];
34+
int minF = f[n - 1], minK = 0;
35+
for (int i = 1; i < k; i++) {
36+
for (int r = n - 1; r >= i; r--) {
37+
f[r] = (int) 1e9;
38+
for (int l = i; l <= r; l++) {
39+
int v = f[l - 1] + mc[l][r];
40+
if (v < f[r]) {
41+
f[r] = v;
42+
fr[i][r] = l;
43+
}
44+
}
45+
}
46+
if (f[n - 1] < minF) {
47+
minF = f[n - 1];
48+
minK = i;
49+
}
50+
}
51+
52+
List<String> path = new ArrayList<>();
53+
for (int i = minK, r = n - 1; i >= 0; i--) {
54+
int l = fr[i][r];
55+
// t := s[l : r+1]
56+
char[] t = Arrays.copyOfRange(s, l, r + 1);
57+
for (int j = 0, m = t.length; j < m / 2; j++) {
58+
char c = t[m - 1 - j];
59+
if (t[j] != c) {
60+
t[j] = c;
61+
}
62+
}
63+
path.add(new String(t));
64+
r = l - 1;
65+
}
66+
Collections.reverse(path);
67+
68+
return minF + System.lineSeparator() +
69+
String.join("+", path);
70+
}
71+
}
72+
/*
73+
D. Palindromes
74+
https://codeforces.com/contest/137/problem/D
75+
76+
灵茶の试炼 2025年03月05日
77+
题目大意:
78+
输入长度 ≤500 的字符串 s,包含大小写英文字母。
79+
输入 k(1≤k≤|s|)。
80+
每次操作,你可以把一个 s[i] 改成任意字符。
81+
把 s 分割为至多 k 个非空子串,要求每个子串都是回文串,最少要修改多少次?
82+
第一行,输出最少修改次数。
83+
第二行,输出修改后的字符串,并用 '+' 表示分割位置,详见样例。
84+
如果有多种分割方案,输出其中任意一种。
85+
86+
rating 1900
87+
本题是 力扣 1278. 分割回文串 III 的变形题,改成【至多】,并要求输出具体方案。
88+
状态定义见我的力扣题解。
89+
把【恰好】改成【至多】,并没有太大区别。在计算 DP 的过程中,每个 f[i][n-1] 都可以是答案,取其中的最小值。
90+
此外,DP 的过程中记录转移来源 from,方便输出具体方案。
91+
算完 DP 后,从终点出发,顺着 from 走到起点(i=0),过程中记录分割出了哪些子串(改成回文串)。
92+
注:这里的终点指对应着最小 f[i][n-1] 的 (i,n-1)。
93+
代码 https://codeforces.com/problemset/submission/137/309000409
94+
代码备份(洛谷)
95+
======
96+
97+
Input
98+
abacaba
99+
1
100+
Output
101+
0
102+
abacaba
103+
104+
Input
105+
abdcaba
106+
2
107+
Output
108+
1
109+
abdcdba
110+
111+
Input
112+
abdcaba
113+
5
114+
Output
115+
0
116+
a+b+d+c+aba
117+
118+
Input
119+
abacababababbcbabcd
120+
3
121+
Output
122+
1
123+
abacaba+babab+bcbabcb
124+
*/

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /