题目是LOJ2347-LOJ2351
「JOI 2018 Final」寒冬暖炉
贪心小水题。选最大的间隔即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int n, k, T[maxn], ans, a[maxn];
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
int main()
{
n = gi(); k = gi(); --k;
for (int i = 1; i <= n; ++i) T[i] = gi();
for (int i = 2; i <= n; ++i) a[i] = T[i] - (T[i - 1] + 1);
ans = T[n] - T[1] + 1;
sort(a + 1, a + n + 1, greater<int>());
for (int i = 1; i <= k; ++i) ans -= a[i];
printf("%d\n", ans);
return 0;
}
「JOI 2018 Final」美术展览
贪心小水题。用前缀和转化式子然后选最大最小即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
const int maxn = 500005;
int n;
lint Max[maxn], sum[maxn], ans;
struct node
{
lint A, B;
bool operator < (const node &x) {
return A < x.A;
}
} p[maxn];
inline lint gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
lint sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
int main()
{
n = gi();
for (int i = 1; i <= n; ++i) p[i].A = gi(), p[i].B = gi();
sort(p + 1, p + n + 1);
Max[0] = -(1ll << 60);
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + p[i].B;
Max[i] = max(Max[i - 1], p[i].A - sum[i - 1]);
}
lint Min = 1ll << 60;
for (int i = n; i >= 1; --i) {
if (Min > p[i].A - sum[i]) Min = p[i].A - sum[i];
ans = max(ans, Max[i] - Min);
}
printf("%lld\n", ans);
return 0;
}
「JOI 2018 Final」团子制作
非常有意思的一道DP。考虑不在同一对角线的团子串不会互相影响,所以在对角线上进行DP。状态为横放/竖放/不放。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3005;
int n, m;
char s[maxn][maxn];
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
inline int check(int x, int y)
{
return (s[x - 1][y] == 'R' && s[x][y] == 'G' && s[x + 1][y] == 'W') << 1 |
(s[x][y - 1] == 'R' && s[x][y] == 'G' && s[x][y + 1] == 'W');
}
int main()
{
n = gi(); m = gi();
for (int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
register int x, y, t, A, B, C, g, ans = 0;
for (int i = 1; i <= n + m - 1; ++i) {
x = min(i, n), y = max(1, i - n + 1), A = 0, B = 0, C = 0;
while (x && y <= m) {
t = check(x, y), g = C;
C = max(C, max(A, B));
if (t & 1) A = max(A, g) + 1;
if (t & 2) B = max(B, g) + 1;
--x; ++y;
}
ans += max(A, max(B, C));
}
printf("%d\n", ans);
return 0;
}
「JOI 2018 Final」月票购买
考虑一定存在一种方案,使得 s − t , u − v s-t,u-v s−t,u−v的最短路的交集一定是连续的。
所以隐式建出 s − t s-t s−t的最短路DAG,选出一条 s − t s-t s−t的最短路上的两个点 x , y x,y x,y,使得 d i s ( u , x ) + d i s ( y , v ) dis(u,x)+dis(y,v) dis(u,x)+dis(y,v)最小。
答案就是上式与 d i s ( u , v ) dis(u,v) dis(u,v)取 m i n min min。
#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
const int maxn = 100005;
int n, m, s, t, u, v;
struct edge
{
int to, next, w;
} e[maxn * 4];
int h[maxn], tot;
lint dis_s[maxn], dis_t[maxn], dis_u[maxn], dis_v[maxn];
bool vis[maxn];
priority_queue<pair<lint, int>, vector<pair<lint, int> >, greater<pair<lint, int> > > que;
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
inline void add(int u, int v, int w)
{
e[++tot] = (edge) {v, h[u], w}; h[u] = tot;
e[++tot] = (edge) {u, h[v], w}; h[v] = tot;
}
void dijkstra(int s, lint *dis)
{
memset(dis + 1, 63, sizeof(lint) * n);
memset(vis + 1, 0, sizeof(bool) * n);
dis[s] = 0;
que.push(make_pair(dis[s], s));
int u;
while (!que.empty()) {
u = que.top().second; que.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
if (dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
que.push(make_pair(dis[v], v));
}
}
}
lint Min_u[maxn], Min_v[maxn], ans = 1ll << 60;
void dfs(int x)
{
if (vis[x] == 1) return ;
vis[x] = 1;
Min_u[x] = dis_u[x];
Min_v[x] = dis_v[x];
for (int i = h[x], y; y = e[i].to, i; i = e[i].next)
if (dis_s[x] + dis_t[y] + e[i].w == dis_s[t]) {
dfs(y);
if (Min_u[x] + Min_v[x] > min(dis_u[x], Min_u[y]) + min(dis_v[x], Min_v[y])) {
Min_u[x] = min(dis_u[x], Min_u[y]);
Min_v[x] = min(dis_v[x], Min_v[y]);
}
}
ans = min(ans, dis_v[x]);
}
int main()
{
n = gi(); m = gi();
s = gi(); t = gi(); u = gi(); v = gi();
for (int u, v, i = 1; i <= m; ++i) u = gi(), v = gi(), add(u, v, gi());
dijkstra(s, dis_s);
dijkstra(t, dis_t);
dijkstra(u, dis_u);
dijkstra(v, dis_v);
memset(vis + 1, 0, sizeof(bool) * n);
dfs(s);
printf("%lld\n", min(Min_u[s] + Min_v[s], dis_u[v]));
return 0;
}
「JOI 2018 Final」毒蛇越狱
比较巧妙的题目。
可以发现,询问串中的'0','1','?'
出现次数最少的字符的出现次数不会超过
6
6
6。
那么如果'0'
或'1'
出现次数最少,那么可以用高维前缀和进行容斥。否则,直接枚举'?'
代表的数统计答案即可。
时间复杂度: O ( 2 n n + 2 6 n ) O(2^nn+2^6n) O(2nn+26n)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 21;
char s[1 << maxn];
int n, L, q, f[1 << maxn], g[1 << maxn], w[1 << maxn], bcnt[1 << maxn];
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
int main()
{
n = gi(); L = 1 << n; q = gi();
scanf("%s", s);
for (int i = 0; i < L; ++i) w[i] = f[i] = g[i] = s[i] - '0', bcnt[i] = bcnt[i >> 1] + (i & 1);
for (int i = 1; i < L; i <<= 1)
for (int j = 0; j < L; ++j)
if (j & i) f[j] += f[i ^ j], g[i ^ j] += g[j];
int p0, p1, p2, ans;
while (q--) {
scanf("%s", s);
p0 = 0; p1 = 0; p2 = 0; ans = 0;
for (int i = 0; i < n; ++i)
if (s[n - i - 1] == '0') p0 |= 1 << i;
else if (s[n - i - 1] == '1') p1 |= 1 << i;
else p2 |= 1 << i;
if (bcnt[p0] <= 6) {
for (int s = p0; ; s = (s - 1) & p0) {
if (bcnt[s] & 1) ans -= g[s | p1];
else ans += g[s | p1];
if (!s) break;
}
} else if (bcnt[p1] <= 6) {
for (int s = p1; ; s = (s - 1) & p1) {
if (bcnt[s ^ p1] & 1) ans -= f[s | p2];
else ans += f[s | p2];
if (!s) break;
}
} else {
for (int s = p2; ; s = (s - 1) & p2) {
ans += w[p1 | s];
if (!s) break;
}
}
printf("%d\n", ans);
}
return 0;
}