《信息学奥赛一本通·高手专项训练》集训 Day 8
luckydrawbox · · 个人记录
哈希
\color{#F39C11}\text{A. 不同子串}
题目
统计一个长度为
题解
先求整个字符串的
本题数据较强,要使用双
代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e5 + 10;
int n, m, ans = 1;
string s;
ll P1 = 13331, P2 = 131, P3 = 1e9 + 7, P4 = 998244353;
ll a1, a2, a3, a4, b[N];
map<ll, bool> h1, h2, h3, h4;
ll qmi(int a, ll p, ll mod) {
ll ans = 1;
while (a) {
if (a & 1)
ans = ans * p % mod;
p = p * p % mod;
a >>= 1;
}
return ans;
}
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
n = read();
m = read();
cin >> s;
for (int i = 0; i < m; i++) {
a1 = (a1 * P1 % P3 + (s[i] - 'a')) % P3;
a2 = (a2 * P2 % P4 + (s[i] - 'a')) % P4;
}
h1[a1] = h2[a2] = 1;
for (int i = 1; i + m - 1 < n; i++) {
a1 = ((a1 - ((ll)(s[i - 1] - 'a')) % P3 * qmi(m - 1, P1, P3) % P3) % P3 + P3) % P3;
a2 = ((a2 - ((ll)(s[i - 1] - 'a')) % P4 * qmi(m - 1, P2, P4) % P4) % P4 + P4) % P4;
a1 = (a1 * P1 % P3 + (s[i + m - 1] - 'a')) % P3;
a2 = (a2 * P2 % P4 + (s[i + m - 1] - 'a')) % P4;
if (h1[a1] && h2[a2])
continue;
h1[a1] = h2[a2] = 1;
ans++;
}
write(ans);
return 0;
}
\color{#FFC116}\text{B. 还原字符串}
题目
有一个字符串
选择一个非负整数
现在给定
题解
枚举
记得分类讨论。
如果我们把
注意一定不要在代码里写上这个:unsigned long long mp[N]={1};,不然会
代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e6 + 10;
int n, pos;
ull sum[N], p = 27, mp[N], s1, s2, ans;
string s, t;
int main() {
freopen("reduction.in", "r", stdin);
freopen("reduction.out", "w", stdout);
n = read();
if (n % 2 == 0) {
puts("NOT POSSIBLE");
return 0;
}
cin >> s;
mp[0] = 1;
for (int i = 1; i <= n; i++) {
mp[i] = mp[i - 1] * p;
sum[i] = sum[i - 1] * p + s[i - 1] - 'A' + 1;
}
for (int i = 1; i <= n / 2; i++) {
s1 = sum[n] - sum[i] * mp[n - i] + sum[i - 1] * mp[n - i];
s2 = (sum[n] - sum[n / 2 + 1] * mp[n / 2]) * (1 + mp[n / 2]);
if (s1 == s2) {
if (pos == 0) {
pos = i;
ans = s1;
} else if (ans != s1) {
puts("NOT UNIQUE");
return 0;
}
}
}
for (int i = n / 2 + 1; i <= n; i++) {
s1 = sum[n] - sum[i] * mp[n - i] + sum[i - 1] * mp[n - i];
s2 = sum[n / 2] * (1 + mp[n / 2]);
if (s1 == s2) {
if (pos == 0) {
pos = i;
ans = s1;
} else if (ans != s1) {
puts("NOT UNIQUE");
return 0;
}
}
}
if (ans == 0) {
puts("NOT POSSIBLE");
return 0;
}
for (int i = 1; i <= n / 2 + (pos <= n / 2 + 1); i++) {
if (pos != i)
cout << s[i - 1];
}
return 0;
}
\color{#9D3DCF}\text{C. 对称之美}
题目
给定
题解
根据初中学过的全等相关知识,我们知道如果两个多边形对应边相等,对应角相等,那么这两个多边形全等。对称显然也有类似的性质。
于是,我们可以用某种方法求出多边形边和角的表示方法,把他们按边
对于边的表示,我们可以使用不开方的勾股定理,对于角的表示,我们可以使用叉积,设所求角由两条射线
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 4e5 + 10;
const ll mod = 998244353;
int t, n, ans;
ll x[N], y[N], hz[N], hf[N], e[N], P = 13331, mp[N] = { 1 };
void jiao(int p, int u) {
int q = p + 1, r = p + 2;
if (q > n)
q -= n;
if (r > n)
r -= n;
ll a = x[q] - x[p], b = y[q] - y[p];
ll c = x[r] - x[q], d = y[r] - y[q];
e[u] = a * d - b * c;
}
void bian(int p, int u) {
int q = p + 1;
if (q > n)
q -= n;
ll a = x[p] - x[q], b = y[p] - y[q];
e[u] = a * a + b * b;
}
ll calcz(int l, int r) { return hz[r] - hz[l - 1] * mp[r - l + 1] % mod; }
ll calcf(int l, int r) { return hf[l] - hf[r + 1] * mp[r - l + 1] % mod; }
bool check(int p, int len) {
ll a = (calcz(p + 1, p + len) % mod + mod) % mod;
ll b = (calcf(p + len + 2, p + 2 * len + 1) % mod + mod) % mod;
return a == b;
}
int main() {
freopen("beauty.in", "r", stdin);
freopen("beauty.out", "w", stdout);
for (int i = 1; i <= 4e5; i++) mp[i] = mp[i - 1] * P % mod;
t = read();
while (t--) {
n = read();
ans = 0;
for (int i = 1; i <= n; i++) {
x[i] = read();
y[i] = read();
}
for (int i = 1; i <= n; i++) jiao(i, i * 2);
for (int i = 1; i <= n; i++) bian(i, i * 2 - 1);
for (int i = 1; i <= n * 2; i++) e[i + n * 2] = e[i];
for (int i = 1; i <= n * 4; i++) hz[i] = (hz[i - 1] * P % mod + e[i]) % mod;
hf[n * 4] = e[n * 4];
for (int i = n * 4 - 1; i >= 1; i--) hf[i] = (hf[i + 1] * P % mod + e[i]) % mod;
for (int i = 1; i <= n; i++) ans += check(i, n - 1);
write(ans);
putchar('\n');
}
return 0;
}
KMP算法
\color{#FFC116}\text{A. 偶串之和}
题目
给你一个字符串,求所有长度为偶数的前缀在整个字符串出现的次数和。
题解
先进行
最后对所有
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e6 + 10;
string s;
int f[N];
ll ans, a1;
struct KMP {
int nxt[N];
void qnext(string a) {
int l1 = a.size();
for (int i = 1, j; i < l1; i++) {
j = nxt[i];
if (i & 1)
f[i + 1]++;
while (j && a[j] ^ a[i]) j = nxt[j];
nxt[i + 1] = (a[i] ^ a[j] ? 0 : j + 1);
f[i + 1] += f[j + 1];
}
}
} kmp;
int main() {
freopen("prefix.in", "r", stdin);
freopen("prefix.out", "w", stdout);
cin >> s;
kmp.qnext(s);
for (int i = 0; i <= s.size(); i++)
ans += f[i + 1];
write(ans);
return 0;
}
\color{#FFC116}\text{B. 密文翻译}
题目
给出一段字母,它的前半段由密文组成,后半段由明文的某一个前缀(可以为空)组成,现在你有一个加密字母表格,让你还原完整的内容(也就是密文+明文的形式)。 如果有多种可能,我们认为那个最短的信息是完整的内容。
题解
把原字符串
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e6 + 10;
string s, s_, t, str;
int p;
char c[N];
struct KMP {
int nxt[N];
void qnext(string a) {
int l1 = a.size();
for (int i = 1, j; i < l1; i++) {
j = nxt[i];
while (j && a[j] ^ a[i]) j = nxt[j];
nxt[i + 1] = (a[i] ^ a[j] ? 0 : j + 1);
}
}
} kmp;
int main() {
freopen("recover.in", "r", stdin);
freopen("recover.out", "w", stdout);
cin >> s_ >> s;
for (int i = 0; i < s_.size(); i++) c[s_[i] - 'a'] = i + 'a';
for (int i = 0; i < s.size(); i++) t += c[s[i] - 'a'];
str = t + "#*#" + s;
kmp.qnext(str);
p = kmp.nxt[str.size()];
while (p > s.size() / 2) p = kmp.nxt[p];
cout << s;
for (int i = p; i < t.size() - p; i++) cout << t[i];
return 0;
}
\color{#9D3DCF}\text{C. 撰写博客}
题目
小明发了一篇博客,由
但是写完这篇博客之后,小明发现博客由于包含一些词语,被自动隐藏了。
具体地,这些词语有
小明发现,只要包含了其中某个词语,博客就会被自动隐藏。换言之,对于任意
于是小明决定在原来的博客
小明发现如果她把第
小明想要知道,如何替换可以得到一篇不会被自动隐藏的博客,而价值的减少量最少。
值得一提的是,如果小明把一个字母替换成空格,那么与之相邻的两个字母也不会变得连续。
但是小明请你帮他求出价值的最少减少量。
题解
处理出所有字符串的
用单调队列优化即可。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
long long read() {
long long x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e5 + 10;
int n, m, a[N], q[N], l = 1, r = 1;
string s, t[15];
int pre[N];
ll mod = 998244353, p = 13331, mp[N], has[N], hasht[N], dp[N];
ll calc(ll l, ll r) { return ((has[r] - has[l - 1] * mp[r - l + 1] % mod) % mod + mod) % mod; }
int main() {
freopen("blog.in", "r", stdin);
freopen("blog.out", "w", stdout);
n = read();
m = read();
cin >> s;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) cin >> t[i];
mp[0] = 1;
for (int i = 1; i <= n; i++) mp[i] = mp[i - 1] * p % mod;
for (int i = 1; i <= n; i++) has[i] = (has[i - 1] * p % mod + s[i - 1] - 'a') % mod;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= t[i].size(); j++) {
hasht[i] = (hasht[i] * p % mod + t[i][j - 1] - 'a') % mod;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i + t[j].size() - 1 > n + 1)
continue;
if (calc(i, i + t[j].size() - 1) == hasht[j])
pre[i + t[j].size() - 1] = max(pre[i + t[j].size() - 1], i);
}
}
for (int i = 1; i <= n + 1; i++) pre[i] = max(pre[i], pre[i - 1]);
for (int i = 1; i <= n + 1; i++) {
while (l <= r && q[l] < pre[i - 1]) l++;
dp[i] = dp[q[l]] + a[i];
while (l <= r && dp[q[r]] >= dp[i]) r--;
q[++r] = i;
}
write(dp[n + 1]);
return 0;
}