\(\text{T1:}\)
题面:戳这里
\(Solution:\)
先跑两次 \(DP\),预处理出两个东西:
- \(f1[i]\) 表示以第 \(i\) 个元素结尾的最大子段和(正序推)
- \(f2[i]\) 表示以第 \(i\) 个元素开头的最大子段和(逆序推)
然后考虑枚举一个下标 \(i\),用于确定绝对选不到的那 \(k\) 个元素。
然后我们就需要知道在当前枚举的这个区域左边的 \(\max(f1[i])\) 以及右边的 \(\max(f2[i])\)。
这个可以开两个 \(\text{ST}\) 表来实现我居然调了半个多小时。
\(AC\)代码:
/*--------------------------------
Code name: REIGN.cpp
Author: The Ace Bee
This code is made by The Ace Bee
--------------------------------*/
#include <cmath>
#include <cstdio>
#include <cstring>
#define rg register
#define int long long
const int MAXN = 100010;
inline int max(int a, int b) { return a > b ? a : b; }
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
int n, k, a[MAXN];
int fp[MAXN], fn[MAXN];
int stp[MAXN][22], stn[MAXN][22];
inline int askp(int l, int r) {
int x = log(r - l + 1) / log(2);
return max(stp[l][x], stp[r - (1 << x) + 1][x]);
}
inline int askn(int l, int r) {
int x = log(r - l + 1) / log(2);
return max(stn[l][x], stn[r - (1 << x) + 1][x]);
}
signed main() {
for (rg int T = read(); T; --T) {
n = read(), k = read();
for (rg int i = 1; i <= n; ++i) a[i] = read();
memset(stp, 0xaf, sizeof stp);
memset(stn, 0xaf, sizeof stn);
for (rg int i = 1; i <= n; ++i) stp[i][0] = max(stp[i - 1][0] + a[i], a[i]);
for (rg int i = n; i >= 1; --i) stn[i][0] = max(stn[i + 1][0] + a[i], a[i]);
for (rg int j = 1; j <= 20; ++j)
for (rg int i = 1; i <= n - (1 << j) + 1; ++i) {
stp[i][j] = max(stp[i][j - 1], stp[i + (1 << (j - 1))][j - 1]);
stn[i][j] = max(stn[i][j - 1], stn[i + (1 << (j - 1))][j - 1]);
}
int ans = -1e18;
for (rg int i = 2; i + k <= n; ++i) {
int xp = askp(1, i - 1);
int xn = askn(i + k, n);
ans = max(ans, xp + xn);
}
printf("%lld\n", ans);
}
return 0;
}
\(\text{T2:}\)
题面:戳这里
\(Solution:\)
首先我们需要倒着做,不然十分不好维护。
然后我们删边从后往前,每次都把那些可能影响答案的点丢去 \(\text{BFS}\)。
删边的话可以用 \(\text{set}\)实现。
其实蛮好写的,但是考试时没多少时间了。。。\(QwQ\)
\(AC\) 代码:
/*--------------------------------
Code name: TRIPS.cpp
Author: The Ace Bee
This code is made by The Ace Bee
--------------------------------*/
#include <set>
#include <queue>
#include <cstdio>
#define rg register
using namespace std;
const int MAXN = 200010;
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
int n, m, k, now;
int ans[MAXN], dgr[MAXN], del[MAXN];
set < int > G[MAXN];
pair < int, int > edge[MAXN];
inline void bfs(int s) {
if (dgr[s] >= k || del[s]) return;
queue < int > q;
--now, del[s] = 1, q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
for (auto v : G[u]) {
--dgr[v];
if (dgr[v] < k && !del[v])
q.push(v), del[v] = 1, --now;
}
}
}
int main() {
n = read(), m = read(), k = read();
for (rg int u, v, i = 1; i <= m; ++i) {
u = read(), v = read();
++dgr[u], ++dgr[v];
edge[i] = make_pair(u, v);
G[u].insert(v), G[v].insert(u);
}
now = n;
for (rg int i = 1; i <= n; ++i) bfs(i);
ans[m] = now;
for (rg int i = m; i >= 1; --i) {
int u = edge[i].first, v = edge[i].second;
if (!del[u]) --dgr[v];
if (!del[v]) --dgr[u];
G[u].erase(v), G[v].erase(u);
bfs(u), bfs(v), ans[i - 1] = now;
}
for (rg int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
return 0;
}
\(\text{T3}\)
题面:戳这里
\(Solution\)
考虑建图,将两个相邻的格子之间建有向边,完了直接 \(\text{Floyd}\) 判断连通性即可。
\(AC\) 代码:
/*--------------------------------
Code name: SCC.cpp
Author: The Ace Bee
This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#include <cstring>
#define rg register
const int MAXN = 410;
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
char rr[25], cc[25];
int r, c, pos[30][30], f[MAXN][MAXN];
int main() {
r = read(), c = read();
for (rg int i = 1; i <= r; ++i)
for (rg int j = 1; j <= c; ++j)
pos[i][j] = (i - 1) * c + j;
char rr[30]; scanf("%s", rr + 1);
char cc[30]; scanf("%s", cc + 1);
for (rg int i = 1; i <= r; ++i) {
if (rr[i] == '>')
for (rg int j = 1; j < c; ++j) f[pos[i][j]][pos[i][j + 1]] = 1;
else
for (rg int j = 1; j < c; ++j) f[pos[i][j + 1]][pos[i][j]] = 1;
}
for (rg int j = 1; j <= c; ++j) {
if (cc[j] == 'v')
for (rg int i = 1; i < r; ++i) f[pos[i][j]][pos[i + 1][j]] = 1;
else
for (rg int i = 1; i < r; ++i) f[pos[i + 1][j]][pos[i][j]] = 1;
}
for (rg int k = 1; k <= pos[r][c]; ++k)
for (rg int i = 1; i <= pos[r][c]; ++i)
for (rg int j = 1; j <= pos[r][c]; ++j)
if (i != j && j != k && i != k)
f[i][j] = f[i][j] | (f[i][k] & f[k][j]);
for (rg int i = 1; i <= pos[r][c]; ++i)
for (rg int j = 1; j <= pos[r][c]; ++j)
if (i != j && !f[i][j]) return puts("NO"), 0;
puts("YES");
return 0;
}
\(\text{T4:}\)
题面:戳这里
\(Solution:\)
其实这是一道大大的数学题
考虑几何概型。
首先我们要明确有解的条件: 将长度为 \(k\) 的一条线段分割成 \(n\) 段,使得这 \(n\) 段可以组成一个闭合的多边形。
这里首先要来一个小结论:周长为 \(k\) 的 \(n\) 边形,最大的一条边长小于 \(k/2\)
小小的证明:
如果存在一条边它的长度大于等于 \(k/2\),那么其他所有边的长度之和都比这一条边小,显然不合理。
然后,我们如果要把一条线段给分成 \(n\) 段,也就是要在线段上取 \(n-1\) 个点。
然后我们发现正着搞不好搞,考虑补集思想。
根据我们之前的小结论,不合法的情况当且仅当存在一条线段的模大于等于 \(k/2\)
也就是说,我们在线段上取的这 \(n-1\) 个点都必须落在线段的同一半边。
这个概率可以直接算: \(p=C_n^1/2^{n-1}\)
(分子是确定一条不合法的长过 \(k/2\) 的边,分母是 \(n-1\) 个点都以 \(1/2\) 的概率落在线段的同一半一边的概率)
然后用 \(1\) 去减就好了: \(Ans=1-n/2^{n-1}\)
\(AC\) 代码:
/*--------------------------------
Code name: EQUILIBER.cpp
Author: The Ace Bee
This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#define rg register
#define int long long
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
const int p = 1e9 + 7;
inline int power(int x, int k) {
int res = 1;
for (; k; k >>= 1, x = 1ll * x * x % p)
if (k & 1) res = 1ll * res * x % p;
return res % p;
}
signed main() {
int n = read(); read();
printf("%lld\n", 1ll * (power(2, n - 1) - n + p) * power(2, (n - 1) * (p - 2)) % p);
return 0;
}
\(\text{T5}\)
题面:戳这里
\(Solution:\)
首先我们发现原数列中,各个数的位置无关紧要。
所以我们只要开个桶就好了。
但是!!!
输入数据有 \(10^9\)...
这里用到一个贪心思想解决:
如果一个数大于了 \(n\times2\),就直接把它丢掉
为什么呢?
首先我们对于该序列只有两种手段,插入一个数或者删除一个数。
而对于大于 \(n\times2\) 的数,删掉它的代价一定优于插入至少 \(n\) 个元素的代价。
所以读入时,先把这些数丢掉。
接下来考虑一下 \(DP\) (好像贪心也能做)
我们记录两个值 \(mtong,maxx\) 分别记录最大的 \(tong\) 的值以及数列中的最大数值。
然后再枚举每一个数值 \(i \in [1,maxx]\) 设 \(f[j]\) 表示枚举到当前时刻
构成以长度为 \(j\) 的排列所需要的最小步数。
考虑初始化和转移:(直接写在代码里面了)
\(AC\) 代码:
/*--------------------------------
Code name: PERMART.cpp
Author: The Ace Bee
This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#include <algorithm>
#define rg register
#define int long long
const int MAXN = 1000010;
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
int n, f[MAXN << 1], tong[MAXN << 1];
signed main() {
for (rg int del, maxx, T = read(); T; --T) {
n = read(), del = 0, maxx = 0;
for (rg int x, i = 1; i <= n; ++i) {
x = read();
if (x > 2 * n) ++del;
else ++tong[x], maxx = std ::max(maxx, x);
}
int mtong = 0;
for (rg int i = 1; i <= maxx; ++i)
mtong = std ::max(mtong, tong[i]);
for (rg int i = 0; i <= mtong; ++i)
f[i] = std ::abs(tong[1] - i);
for (rg int i = 2; i <= maxx; ++i) {
for (rg int j = mtong - 1; ~j; --j) f[j] = std ::min(f[j], f[j + 1]);
for (rg int j = mtong; ~j; --j) f[j] += std ::abs(tong[i] - j);
}
int ans = 1e9;
for (rg int i = 0; i <= mtong; ++i) ans = std ::min(ans, f[i]);
printf("%lld\n", ans + del);
for (rg int i = 0; i <= maxx; ++i) tong[i] = 0;
}
return 0;
}
\(\text{T6:}\)
题面:戳这里
\(Solution:\)
还是考虑建图。
先将相邻的两个点连边。
对于数值的元素,开一个邻接表,为了降低边的数目可以建菊花图傻逼才去建完全图,将边的权处理一下:
- 对于相邻两点之间的边,权为2
- 对于菊花图中的边,权为1
跑完最短路后,把答案除以 \(2\) 即可
\(AC\) 代码:
/*--------------------------------
Code name: DIGJUMP.cpp
Author: The Ace Bee
This code is made by The Ace Bee
--------------------------------*/
#include <queue>
#include <cstdio>
#include <cstring>
#define rg register
#define int long long
const int MAXN = 100100;
const int MAXM = 300010;
using namespace std;
inline int read() {
int s = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
return f ? -s : s;
}
int tot, head[MAXN], nxt[MAXM << 1], ver[MAXM << 1], w[MAXM << 1];
inline void Add_edge(int u, int v, int d)
{ nxt[++tot] = head[u], head[u] = tot, ver[tot] = v, w[tot] = d; }
int tong[15], tt, hd[15], nt[MAXN], vr[MAXN];
inline void Add_relate(int u, int v)
{ nt[++tt] = hd[u], hd[u] = tt, vr[tt] = v; }
int n, exi[MAXN], dis[MAXN];
queue < int > q;
inline void spfa(int s) {
memset(exi, 0, sizeof exi);
memset(dis, 0x7f, sizeof dis);
q.push(s), exi[s] = 1, dis[s] = 0;
while (!q.empty()) {
int u = q.front(); q.pop(), exi[u] = 0;
for (rg int i = head[u]; i; i = nxt[i]) {
int v = ver[i];
if (dis[v] > dis[u] + w[i]) {
dis[v] = dis[u] + w[i];
if (!exi[v]) exi[v] = 1, q.push(v);
}
}
}
}
signed main() {
char s[MAXN];
scanf("%s", s + 1), n = strlen(s + 1);
for (rg int x, i = 1; i <= n; ++i) {
x = s[i] ^ 48, ++tong[x], Add_relate(x, i);
if (i != n) Add_edge(i, i + 1, 2), Add_edge(i + 1, i, 2);
}
for (rg int x = 0; x < 10; ++x)
if (tong[x] > 1)
for (rg int i = hd[x]; i; i = nt[i])
Add_edge(n + x + 1, vr[i], 1), Add_edge(vr[i], n + x + 1, 1);
spfa(1);
printf("%lld\n", dis[n] >> 1);
return 0;
}
完结撒花\(qwq\)