题解 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;
}
posted @ 2024-07-24 02:21  全球通u1  阅读(12)  评论(0编辑  收藏  举报