题解 2018-2019 ICPC Southwestern European Regional Programming Contest (SWERC 2018)
2018-2019 ICPC Southwestern European Regional Programming Contest (SWERC 2018)
在Gym上查看
A
\(O(k\times n)\)直接做也不会超时。
int li[N];
int main()
{
int n = read(), k = read(), ans = 0;
fill_n(li + 1, n, 1);
for (; k--;)
{
int i = read();
for (int j = i; j <= n; j += i)
li[j] ^= 1;
ans = max(ans, n - accumulate(li + 1, li + n + 1, 0));
}
printf("%d", ans);
return 0;
}
B
题目特别提到,上下两行之间必然有连通,而且“all the non-blurred pixels are connected in such a way that any horizontal or vertical line drawn between two non-blurred pixels goes only through non-blurred pixels”
这样就好说了,可以二分答案,检查时假设正方形从每一行开始。由题意,仅检查下边界和左右边界是否满足条件即可。
int l[N], r[N], n;
bool check(int x)
{
for (int i = 1; i <= n; ++i)
{
if (r[i] - l[i] + 1 < x)
continue;
int d = i + x - 1;
if (d > n)
return false;
int L = max(l[i], l[d]);
int R = min(r[i], r[d]);
if (R - L + 1 >= x)
return true;
}
return false;
}
int main()
{
n = read();
for (int i = 1; i <= n; ++i)
l[i] = read(), r[i] = read();
int l = 1, r = n + 1;
while (l < r)
{
int mid = (l + r + 1) / 2;
if (check(mid))
l = mid;
else
r = mid - 1;
}
printf("%d", l);
return 0;
}
D
同一个x的记在一起,把y方向的距离之和差分。
差分两次。
复杂度是|Y|+n。
去年暑假有个类似的题目,值域更大,可以离散化。
#include <bits/stdc++.h>
using namespace std;
#define read() ({int x,c,f=1;while((c=getchar())<48||57<c)if(c=='-')f=-1;for(x=c^48;47<(c=getchar())&&c<58;)x=x*10+(c^48);x*f; })
using ll = long long;
const int N = 2e5 + 50, OFF = 10;
int x[N], y[N], dd[N];
ll d[N];
int main()
{
int X = read(), Y = read();
int n = read();
map<int, pair<int, int>> line;
for (int i = 1; i <= n; ++i)
{
int x = read(), y = read();
if (!line.count(x))
line.insert({x, {y, y}});
else
{
auto [up, down] = line[x];
line[x] = {min(up, y), max(down, y)};
}
}
ll dist = 0;
for (auto [x, pr] : line)
{
auto [up, down] = pr;
// printf("# %d %d %d\n", x, up, down);
dd[up + 1]++;
dd[down + 1]++;
dist += down;
}
d[0] = -line.size();
for (int i = 1; i <= Y; ++i)
d[i] = dd[i] + d[i - 1];
ll mindis = dist;
// printf("@ %d %d %lld\n", dd[0], d[0], dist);
for (int i = 1; i <= Y; ++i)
{
dist += d[i];
mindis = min(mindis, dist);
// printf("@ %d %d %lld\n", dd[i], d[i], dist);
}
printf("%lld", mindis * 2 + X - 1);
return 0;
}
G
题目贴心提醒len < LONG_LONG_MAX,想到瞎搞记忆化搜索。
用map会TLE,unordered_map可过。减少对map的访问以尽可能减小常数。
复杂度大概是$$n^2$$
#include <iostream>
#include <unordered_map>
using namespace std;
#define read() ({ll x;cin>>x;x; })
using ll = long long;
#define int ll
const int N = 2e5 + 2, md = 1e9 + 7;
ll len[N];
string s, tmp;
struct Edge
{
char type;
ll x, y, lo, hi;
} e[N];
unordered_map<ll, ll> mp[N];
ll dfs(int u, ll l)
{
if (l == 0)
return 0;
auto [t, x, y, lo, hi] = e[u];
if (auto it = mp[u].find(l); it != mp[u].end())
return it->second;
if (t == 'A')
{
if (l <= len[x])
return mp[u][l] = dfs(x, l);
else
return mp[u][l] = (dfs(x, len[x]) + dfs(y, l - len[x])) % md;
}
else if (t == 'S')
return mp[u][l] = (dfs(x, lo + l) - dfs(x, lo) + md) % md;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int n = read();
cin >> s;
len[0] = s.length();
for (int i = 1; i < n; ++i)
{
cin >> tmp;
if (tmp[0] == 'S')
{
ll x = read(), lo = read(), hi = read();
len[i] = hi - lo;
e[i] = {'S', x, -1, lo, hi};
}
else if (tmp[0] == 'A')
{
ll x = read(), y = read();
e[i] = {'A', x, y};
len[i] = len[x] + len[y];
}
}
mp[0][0] = 0;
for (int i = 1; i <= len[0]; ++i)
mp[0][i] = (mp[0][i - 1] + s[i - 1]) % md;
std::cout << dfs(n - 1, len[n - 1]);
return 0;
}
E
情况多且杂。一个人最小时可以让其他人尽可能大,反之亦然。
边界条件一开始有漏判。每个人值域之和不含100就是impossible。
int main()
{
int n = read(), sum = 0, zer = 0;
vector<pair<string, int>> mp;
for (int i = n; i--;)
{
string s;
cin >> s;
int l = read();
sum += l;
zer += !l;
mp.push_back({s, l});
}
int rem = 10000 - sum * 100;
int dec = max(-50, -49 * (n - 1) + rem);
// int inc = min(49, 50 * (n - 1) + rem);
// printf("%d %d", dec, inc);
if (sum * 100 - 50 * n > 10000 || sum * 100 + 49 * n < 10000)
{
puts("IMPOSSIBLE");
return 0;
}
for (auto [s, i] : mp)
{
int inc = min(49, 50 * (n - zer - !!i) + rem);
int l = (i * 100 + dec), r = (i * 100 + inc);
l = max(0, l);
r = min(10000, r);
cout << s << ' ' << l / 100 << '.';
printf("%d%d", l / 10 % 10, l % 10);
cout << ' ' << r / 100 << '.';
printf("%d%d\n", r / 10 % 10, r % 10);
}
return 0;
}
H
三维数点。可以“线段树里套set”?
本来想写KD-Tree,复杂度卡得很死$$(n*(n^{1-\frac {1}{d}}+k))$$而作罢。
#include <bits/stdc++.h>
using namespace std;
#define read() ({int x,c,f=1;while((c=getchar())<48||57<c)if(c=='-')f=-1;for(x=c^48;47<(c=getchar())&&c<58;)x=x*10+(c^48);x*f; })
#define fo(i, n) for (int i = 1, _##i(n); i <= _##i; ++i)
const int N = 2e5 + 2, M = 1e7;
int n;
int dis[3][N];
namespace Dijk {
using pii = pair<int, int>;
vector<pii> G[N];
bool vis[N];
void input() {
int edgecnt = read();
while (edgecnt--) {
int a = read(), b = read(), w = read();
G[a].push_back({w, b});
G[b].push_back({w, a});
}
}
struct Node {
int d, u;
bool operator<(const Node &rhs) const { return d > rhs.d; }
};
constexpr int inf = 1e9;
void work(int s) {
auto d = dis[s];
fill(d, d + n + 1, inf);
d[s] = 0;
fill(vis, vis + n + 1, 0);
priority_queue<Node> q;
q.push(Node{0, s});
while (!q.empty()) {
auto [unuse, u] = q.top();
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (auto [w, v] : G[u])
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
q.push({d[v], v});
}
}
}
};
struct Point {
int x, y, z;
bool operator<(const Point &rhs) const {
return x == rhs.x ? (y == rhs.y ? z < rhs.z : y < rhs.y) : x < rhs.x;
}
} d[N];
int cnt[M + 10];
void upd(int i, int k) {
for (; i <= M; i += i & -i)
cnt[i] = min(cnt[i], k);
}
int query(int i) {
int res = 1e9;
for (; i; i -= i & -i)
res = min(res, cnt[i]);
return res;
}
int main() {
n = read();
Dijk::input();
Dijk::work(0);
Dijk::work(1);
Dijk::work(2);
for (int i = 0; i < n; ++i)
d[i] = {dis[0][i] + 1, dis[1][i] + 1, dis[2][i] + 1};
sort(d, d + n);
fill(cnt, cnt + M + 1, 1e9);
int ans = 0;
for (int i = 0; i < n;) {
while (i < n && query(d[i].y) <= d[i].z)
++i;
if (i == n)
break;
++ans;
upd(d[i].y, d[i].z);
++i;
while (i < n && d[i].x == d[i - 1].x && d[i].y == d[i - 1].y && d[i].z == d[i - 1].z)
++i, ans++;
}
printf("%d\n", ans);
return 0;
}
I
先去掉噪声和边界。
对于剩下的黑色像素,判断它们是否形成A, B和C(黑色联通区域)。各自计数。
#include <cstdio>
#define read() ({int x,c,f=1;while((c=getchar())<48||57<c)if(c=='-')f=-1;for(x=c^48;47<(c=getchar())&&c<58;x=x*10+(c^48));x*f; })
#define getc() ({char c;while((c=getchar())!='.'&&c!='#');c=='#'; })
constexpr int dx[] = {0, 0, 1, -1, 1, 1, -1, -1};
constexpr int dy[] = {-1, 1, 0, 0, 1, -1, 1, -1};
constexpr int ld = 8;
constexpr int MAX_SIZE = 1005;
int mp[1005][1005];
bool is_noise(int x, int y) {
if (!mp[x][y])
return false;
for (int i = 0; i < ld; i++) {
if (mp[x + dx[i]][y + dy[i]])
return false;
}
return true;
}
void del_outside(int x, int y) {
if (!mp[x][y])
return;
mp[x][y] = 0;
for (int i = 0; i < ld; i++) {
del_outside(x + dx[i], y + dy[i]);
}
}
bool is_corner(int x, int y) {
return mp[x][y] && !mp[x][y - 1] && !mp[x - 1][y] && mp[x][y + 1] && mp[x + 1][y];
}
int main() {
int m = read(), n = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
mp[i][j] = getc();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (is_noise(i, j))
mp[i][j] = 0;
del_outside(1, 1);
int ans1 = 0, ans2 = 0, ans3 = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (is_corner(i, j)) {
int k1 = 0;
while (mp[i + k1][j + k1])
k1++;
int k2 = 0;
while (!mp[i + k1][j + k1 + k2] && mp[i + k1 - 1][j + k1 + k2])
k2++;
if (!mp[i + k1 - 1][j + k1 + k2])
k2 -= k1;
if (mp[i + k1 + k2][j + k1]) {
if (mp[i + k1 + k2 + k1 + k2][j + k1])
ans2++;
else
ans1++;
} else {
ans3++;
}
}
printf("%d %d %d\n", ans1, ans2, ans3);
return 0;
}
K
用KMP可以求循环节,然后区间DP。
#include <bits/stdc++.h>
using namespace std;
#define read() ({int x,c,f=1;while((c=getchar())<48||57<c)if(c=='-')f=-1;for(x=c^48;47<(c=getchar())&&c<58;)x=x*10+(c^48);x*f; })
#define fo(i, n) for (int i = 1, _##i(n); i <= _##i; ++i)
const int N = 1000;
void kmp(const char *s, int f[])
{
f[0] = f[1] = 0;
for (int i = 1; s[i]; ++i)
{
int j = f[i];
while (j && s[i] != s[j])
j = f[j];
f[i + 1] = (s[i] == s[j] ? j + 1 : 0);
}
}
int n, fail[N][N]; // 从i开始的fail数组
char s[N];
int f[N][N]; // s[i...j]的压缩长度
int main()
{
memset(f, 0x3f, sizeof f);
scanf("%d%s", &n, &s);
for (int i = 0; i < n; ++i)
{
kmp(s + i, fail[i]);
f[i][1] = 1;
}
for (int l = 2; l <= n; ++l)
for (int i = 0; i < n; ++i)
{
int diff = l - fail[i][l];
if (fail[i][l] > 0 && l % diff == 0) // 循环了
{
f[i][l] = min(f[i][l], f[i][diff]);
}
for (int m = 1; m <= l; ++m)
{
f[i][l] = min(f[i][l], f[i][m] + f[i + m][l - m]);
}
}
printf("%d", f[0][n]);
return 0;
}