【2021夏纪中游记】2021.7.12模拟赛
2021.7.12模拟赛
比赛概括:
\(\mathrm{sum}=40+0+0+5\)
唉。
T1 好元素:
题目大意:
找到 \(a_1+a_2+a_3=a_4\) 的个数(\(1,2,3\) 可以相等)。
思路:
一眼题,用将 \(a_1+a_2+a_3=a_4\) 转移为 \(a_1+a_2=a_4-a_3\),然后 \(\mathcal{O}(n^2)\) 加哈希。
但是不能用 map,否则回超时。
代码:
const int N = 5e3 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, mod = 25000003, ans;
ll a[N];
int hash[25000005];
inline int Hash(int val, int op = 1)
{
int x = (val % mod + mod) % mod;
for (; hash[x] != 1010580540 && hash[x] != val; x = (x + 1) % mod);
if (op)
return x;
hash[x] = val;
return 0;
}
int main()
{
freopen("good.in", "r", stdin);
freopen("good.out", "w", stdout);
memset (hash, 60, sizeof hash);
n = Read();
for (register int i = 1; i <= n; i++)
{
a[i] = Read();
for (register int j = 1; j < i; j++)
if(hash[Hash(a[i] - a[j])] == a[i] - a[j]) {ans++; break;}
for (register int j = 1; j <= i; j++)
if(hash[Hash(a[i] + a[j])] != a[i] + a[j]) Hash(a[i] + a[j], 0);
}
printf ("%d\n", ans);
return 0;
}
T2 最短路径:
题目大意:
在平面直角坐标系内的 \(n\) 个点,选择两条路径使得所有点必须被经过,且 \(b1\) 必在路线一、\(b2\) 必在路线二。
思路:
由于确定了路线一,路线二就也确定了,所以一开始想到的状态是设 \(f_{i,j}\) 表示路线一从 \(j\) 来到 \(i\) 的最小值,但是这么设会导致路线二很难连接。
因此,考虑两个路线一起设,设 \(f_{i,j}\) 表示路线一正着走到 \(i\)、路线二倒着走到 \(j\) 的最小数。则有:
其中 \(k=\max(i,j)+1\),\(n\) 时要特判处理。
代码:
const int N = 1010;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, b1, b2;
struct point
{
int x, y, id;
}a[N];
bool cmp (point a, point b)
{
return a.x < b.x;
}
double f[N][N];
double Dis(int i, int j)
{
return sqrt((a[i].x - a[j].x) * (a[i].x - a[j].x) +
(a[i].y - a[j].y) * (a[i].y - a[j].y));
}
int main()
{
freopen("path.in", "r", stdin);
freopen("path.out", "w", stdout);
n = Read(), b1 = Read(), b2 = Read(); b1++, b2 ++;
for (int i = 1; i <= n; i++)
a[i].x = Read(), a[i].y = Read(), a[i].id = i;
sort (a + 1, a + 1 + n, cmp);
a[0] = a[1];
for (int i = 1; i <= n; i++)
{
if (a[i].id == b1) b1 = a[i].id;
if (a[i].id == b2) b2 = a[i].id;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
f[i][j] = 1e9;
f[1][1] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
int k = max(i, j) + 1;
if (max(i, j) == n)
{
if (i == n) f[n][n] = min(f[n][n], f[n][j] + Dis(j, n));
else f[n][n] = min(f[n][n], f[i][n] + Dis(i, n));
continue;
}
if (k != b1) f[i][k] = min(f[i][k], f[i][j] + Dis(j, k));
if (k != b2) f[k][j] = min(f[k][j], f[i][j] + Dis(i, k));
}
printf ("%.2lf\n", f[n][n]);
return 0;
}
T3 [GDOI2016]最长公共子串:
题目大意:
给两个字符串 \(s,t\)。可对 \(s\) 的一些区间进行任意排列,使得 \(s,t\) 的最长公共子串最大,求其长度。
正文:
难确实难。有一个很显然的性质,有交集的区间就让它们并在一起,使范围缩小到 \(O(n)\) 级。
然后考虑匹配,由于区间内的可以按任意顺序,我们就把区间看作一个整体,匹配就是和 \(t\) 比字母数量。设 \(f_{i,j}\) 表示 \(s\) 在 \(i\) 区间、\(t\) 从 \(j\) 字符开始的最长长度。那么就照上文说的,比字母数量。
得到了 \(f\) 后,就能得到 \(g_{i,j}\) 表示 \(s\) 从 \(i\) 区间开始、\(t\) 从 \(j\) 字符开始的最长长度。
然后随便统计答案。
总结:本题思路以小见大(\(f\rightarrow g\))。
代码:
const int N = 2010, M = 1e5 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
char s[N], t[N];
int n, m, k;
struct node
{
int l, r;
bool operator < (const node &a) const
{
return l < a.l;
}
}a[M];
int num[N][27];
int f[N][N], g[N][N];
int bucket[27];
int tot, ans;
int main()
{
freopen("lcs.in", "r", stdin);
freopen("lcs.out", "w", stdout);
scanf("%s%s", t + 1, s + 1);
n = strlen(t + 1), m = strlen(s + 1);
k = Read();
for (int i = 1; i <= k; i++)
a[i].l = Read() + 1, a[i].r = Read() + 1;
for (int i = 1; i <= n; i++)
a[++k] = (node) {i, i};
sort (a + 1, a + 1 + k);
tot = 1;
for (int i = 2; i <= k; i++)
{
if (a[i].l <= a[tot].r) a[tot].r = max(a[tot].r, a[i].r);
else a[++tot] = a[i];
}
k = tot;
for (int i = 1; i <= k; i++)
for (int j = a[i].l; j <= a[i].r; j++)
num[i][s[j] - 'a']++;
for (int i = k; i; i--)
for (int j = m; j; j--)
{
int len = a[i].r - a[i].l + 1, l = j;
for (; l <= min(m, len + j - 1); l++)
{
if (num[i][t[l] - 'a'] == bucket[t[l] - 'a']) break;
bucket[t[l] - 'a'] ++;
}
f[i][j] = l - j;
if (f[i + 1][l] == a[i + 1].r - a[i + 1].l + 1)
g[i][j] = f[i][j] + g[i + 1][l];
else g[i][j] = f[i][j] + f[i + 1][l];
for (int p = j; p <= l; p++) // [j,l] 是当前区间绝对可以匹配的,找下一个区间最优匹配
{
if (p != l) bucket[t[p] - 'a'] --;
if (f[i + 1][p] == a[i + 1].r - a[i + 1].l + 1)
ans = max(ans, p - j + g[i + 1][p]);
else ans = max(ans, p - j + f[i + 1][p]);
}
}
printf ("%d\n", ans);
return 0;
}
T4 Vani和Cl2捉迷藏:
题目大意:
最大独立集板题。
正文:
板题不讲。
代码:
const int N = 210, M = 30010;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, k;
bool G[N][N];
int con[N];
bool vis[N];
bool Hungary(int u)
{
for (int v = 1; v <= n; v++)
{
if (!G[u][v]) continue;
if (vis[v]) continue;
vis[v] = 1;
if (!con[v] || Hungary(con[v]))
{
con[v] = u;
return 1;
}
}
return 0;
}
int main()
{
// freopen("travel10.in", "r", stdin);
// freopen(".out", "w", stdout);
n = Read(), k = Read();
for (int i = 1, u, v; i <= k; i++)
u = Read(), v = Read(), G[u][v] = 1;
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (i != k)
for (int j = 1; j <= n; j++)
if (j != i && j != k)
G[i][j] |= G[i][k] & G[k][j];
int ans = 0;
for (int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof vis);
if(Hungary(i)) ans++;
}
printf ("%d\n", n - ans);
return 0;
}