【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\) 的最小數。則有:
\[\begin{aligned} f_{i,k}&=\min\{f_{i,j}+\mathrm{dis}(j,k)\}\\ f_{k,j}&=\min\{f_{i,j}+\mathrm{dis}(i,k)\} \end{aligned}\]其中 \(k=\max(i,j)+1\)
程式碼:
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;
}