Atcoder CODE FESTIVAL 2017 Final 简要题解

来源:互联网 发布:电脑软件搬家 编辑:程序博客网 时间:2024/06/05 23:56

AKIBA

模拟。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}string s, t;int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    cin >> s;    for (int i = 0; i < s.length(); t.pb(s[i ++]))        if ((s[i] == 'K' || s[i] == 'B' || s[i] == 'R') && (!i || s[i - 1] != 'A'))            t.pb('A');    if (t.back() != 'A')        t.pb('A');    return puts(t == "AKIHABARA" ? "YES" : "NO"), 0;}

Palindrome-phobia

最后一定是”abcabc”的形式,那么最大出现次数减去最小出现次数不超过1

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 100005;char s[MAXN];int a[3];int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    scanf("%s", s);    for (int i = 0; s[i]; i ++)        a[s[i] - 'a'] ++;    return puts(*max_element(a, a + 3) - *min_element(a, a + 3) <= 1 ? "YES" : "NO"), 0;}

Time Gap

如果i出现3次答案一定是0,出现2次一定是i24i,剩下直接暴力枚举。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 55;int n, ans, cnt[MAXN];bool v[MAXN];inline void DFS(int x){    if (x == 13)    {        int ret = 24;        for (int i = 0; i < 24; i ++)            for (int j = i + 1; j < 24; j ++)                if (v[i] && v[j])                    ret = min(ret, min(j - i, 24 - j + i));        ans = max(ans, ret);        return ;    }    if (cnt[x] == 2)        v[x] = v[24 - x] = 1, DFS(x + 1);    else if (cnt[x])    {        v[x] = 1, DFS(x + 1), v[x] = 0;        if (x)            v[24 - x] = 1, DFS(x + 1), v[24 - x] = 0;    }    else        DFS(x + 1);}int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    n = Read();    for (int i = 1; i <= n; i ++)        cnt[Read()] ++;    cnt[0] ++;    for (int i = 1; i < 12; i ++)        if (cnt[i] > 2)            return puts("0"), 0;    if (cnt[12] > 1 || cnt[0] > 1)        return puts("0"), 0;    DFS(0);    return printf("%d\n", ans), 0;}

Zabuton

考虑确定一个顺序之后DP,不难发现顺序就是H+P升序。

fi表示前面用i个物品堆出的最小高度。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 5005;struct Node{    int p, h;    bool operator < (const Node &b) const    {        return p + h < b.p + b.h;    }} a[MAXN];LL f[MAXN];int n;int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    n = Read();    for (int i = 1; i <= n; i ++)        a[i].h = Read(), a[i].p = Read(), f[i] = 1LL << 60;    sort(a + 1, a + n + 1);    for (int i = 1; i <= n; i ++)        for (int j = i; j; j --)            if (f[j - 1] <= a[i].h)                f[j] = min(f[j], f[j - 1] + a[i].p);    for (int i = n; ~i; i --)        if (f[i] ^ 1LL << 60)            return printf("%d\n", i), 0;}

Combination Lock

考虑差分,那么每次相当于给al1ar+11

一个连通块模26的总和是不变的,回文的限制可以认为是a1+an+1=0,a2+an=0...,可以认为加上了[1,n],[2,n1]...这些区间,最后让所有数都变成0,并查集维护。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 100005;int n, m, a[MAXN], b[MAXN], f[MAXN];char s[MAXN];inline int Find(int x){    while (x ^ f[x])        x = f[x] = f[f[x]];    return x;}int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    scanf("%s", s + 1), n = strlen(s + 1), m = Read();    for (int i = 1; i <= n; i ++)        a[i] = s[i] - 'a';    for (int i = n + 1; i; i --)        f[i] = i, a[i] = (a[i] - a[i - 1] + 26) % 26;    for (int i = 1, l, r; i <= m; i ++)        l = Read(), r = Read(), f[Find(l)] = Find(r + 1);    for (int i = 1; i <= n + 1; i ++)        f[Find(i)] = Find(n + 2 - i);    for (int i = 1; i <= n + 1; i ++)        b[Find(i)] = (b[Find(i)] + a[i]) % 26;    for (int i = 1; i <= n + 1; i ++)        if (Find(i) == i && b[i])            return puts("NO"), 0;    return puts("YES"), 0;}

Distribute Numbers

考虑相等数的对数,不难得出(N2)=(K2)N,所以N=K(K1)+1

然后打表求出小范围解,大范围的类似构造一下。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 50;int k = 38, n = k * (k - 1) + 1, a[MAXN][MAXN];int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    printf("%d %d\n", n, k);    for (int i = 1; i <= k; i ++)    {        printf("1");        for (int j = (i - 1) * (k - 1) + 2; j <= i * (k - 1) + 1; j ++)            printf(" %d", j), a[i][j - (i - 1) * (k - 1) - 2] = j;        putchar(10);    }    for (int i = 0; i < k - 1; i ++)        for (int j = 0; j < k - 1; j ++)        {            printf("%d", i + 2);            for (int l = 2, cur = j; l <= k; l ++, cur = (cur + i) % (k - 1))                printf(" %d", a[l][cur]);            putchar(10);        }}

Mancala

最优策略一定是每次选最左边的符合要求的位置。

ti表示i的操作次数,如果ai>i显然ti=0,否则ti=ai+nj=i+1tji

把这个过程DP掉。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 105;const int MAXM = 3505;const int mod = 1e9 + 7;int n, m, ret, lst, f[MAXN][MAXM];int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    n = Read(), m = Read();    ret = n * m * (m + 1) / 2;    for (int i = 1; i < n; i ++)        ret = 1LL * ret * (m + 1) % mod;    f[n + 1][0] = 1;    for (int i = n; i; lst += (lst + min(i, m)) / i, i --)        for (int j = 0; j <= lst; j ++)            for (int k = 0; k <= m; k ++)                if (k <= i)                    f[i][j + (j + k) / i] = (f[i][j + (j + k) / i] + f[i + 1][j]) % mod;                else                    f[i][j] = (f[i][j] + f[i + 1][j]) % mod;    for (int i = 1; i <= lst; i ++)        ret = (ret - 1LL * f[1][i] * i % mod + mod) % mod;    return printf("%d\n", ret), 0;}

Poor Penguin

如果一个位置左上或者左下或者右上或者右下没有冰山,那么这个位置就会坏掉。

还有一种情况是存在一条横线和一条竖线将这里分成四部分,左上右下没有冰山或者右上左下没有冰山,那么两部分就变成了独立的了,可以看题解的图来帮助理解。

DP矩形的四个边界。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 45;const int INF = 0x3f3f3f3f;int n, m, xp, yp, ans, s[MAXN][MAXN], f[MAXN][MAXN][MAXN][MAXN];char a[MAXN][MAXN];inline int Sum(int xl, int xr, int yl, int yr){    return s[xr][yr] + s[xl - 1][yl - 1] - s[xl - 1][yr] - s[xr][yl - 1];}int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    n = Read(), m = Read(), mset(f, INF), ans = INF;    for (int i = 1; i <= n; i ++)        scanf("%s", a[i] + 1);    for (int i = 1; i <= n; i ++)        for (int j = 1; j <= m; j ++)            if (a[i][j] == 'P')                xp = i, yp = j;    for (int i = 1; i <= n; i ++)        for (int j = 1; j <= m; j ++)            s[i][j] = s[i - 1][j] + s[i][j - 1] + (a[i][j] == '#') - s[i - 1][j - 1];    f[1][n][1][m] = 0;    for (int xl = 1; xl <= n; xl ++)        for (int xr = n; xr >= xl; xr --)            for (int yl = 1; yl <= m; yl ++)                for (int yr = m; yr >= yl; yr --)                    if (f[xl][xr][yl][yr] ^ INF)                    {                        int cur = f[xl][xr][yl][yr], val;                        if (xl <= xp && xp <= xr && yl <= yp && yp <= yr)                            ans = min(ans, cur + min(min(Sum(xl, xp, yl, yp), Sum(xl, xp, yp, yr)), min(Sum(xp, xr, yl, yp), Sum(xp, xr, yp, yr))));                        else                            ans = min(ans, cur);                        cur += Sum(xl, xr, yl, yr);                        for (int x = xl; x <= xr + 1; x ++)                            for (int y = yl; y <= yr + 1; y ++)                                if (!((x == xl || x == xr + 1) && (y == yl || y == yr + 1)))                                {                                    val = Sum(xl, x - 1, yl, y - 1) + Sum(x, xr, y, yr);                                    if (x > xl && y > yl && !(x <= xp && xp <= xr && y <= yp && yp <= yr))                                        f[xl][x - 1][yl][y - 1] = min(f[xl][x - 1][yl][y - 1], cur - val);                                    if (x <= xr && y <= yr && !(xl <= xp && xp < x && yl <= yp && yp < y))                                        f[x][xr][y][yr] = min(f[x][xr][y][yr], cur - val);                                    val = Sum(xl, x - 1, y, yr) + Sum(x, xr, yl, y - 1);                                    if (x > xl && y <= yr && !(x <= xp && xp <= xr && yl <= yp && yp < y))                                        f[xl][x - 1][y][yr] = min(f[xl][x - 1][y][yr], cur - val);                                    if (x <= xr && y > yl && !(xl <= xp && xp < x && y <= yp && yp <= yr))                                        f[x][xr][yl][y - 1] = min(f[x][xr][yl][y - 1], cur - val);                                }                    }    return printf("%d\n", ans), 0;}

Full Tournament

对于一组a,当且仅当ST,aSaT,考虑归纳法,只需要证aiai+2n1(0i<2n1)

不妨设i是偶数的情况,令i=2k,注意到ranki的是左边rankk的和右边rankk中的较小者,同时i+2n1是左边k+2n2和右边k+2n2中的较小者,由于两边都有ak<ak+2n2,所以i是四个里面最小的,所以有aiai+2n1,奇数类似。

这相当于存在一个拓扑序关系,求出每个rank可能的最大最小id,扫描线+贪心维护,类似之前的一个CF题。

最后把ranki的放在rev(i)位置即可,rev(x)表示将x的二进制位翻转得到的结果。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 262145;int n, N, R[MAXN], low[MAXN], hig[MAXN], val[MAXN];priority_queue <pii, vector <pii>, greater <pii>> q;vector <pii> eve[MAXN];int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    n = Read(), N = 1 << n;    for (int i = 1; i < N; i ++)        R[i] = (R[i >> 1] >> 1) | ((i & 1) << n - 1);    for (int i = 0; i < N; i ++)        val[i] = Read() - 1;    for (int i = N - 1; ~i; i --)    {        hig[i] = N - 1;        if (~val[i])            hig[i] = val[i];        for (int j = 0; j < n; j ++)            if (!(i >> j & 1))                hig[i] = min(hig[i], hig[i ^ 1 << j]);    }    for (int i = 0; i < N; i ++)    {        low[i] = 0;        if (~val[i])            low[i] = val[i];        for (int j = 0; j < n; j ++)            if (i >> j & 1)                low[i] = max(low[i], low[i ^ 1 << j]);    }    for (int i = 0; i < N; i ++)        if (low[i] > hig[i])            return puts("NO"), 0;        else            eve[low[i]].pb(mp(hig[i], i));    for (int i = 0; i < N; i ++)    {        for (auto e : eve[i])            q.push(e);        if (q.empty() || q.top().xx < i)            return puts("NO"), 0;        val[q.top().yy] = i, q.pop();    }    puts("YES");    for (int i = 0; i < N; i ++)        printf("%d%c", val[R[i]] + 1, i == N - 1 ? '\n' : ' ');    return 0;}

Tree MST

直接点分,每个子树连O(size)条边即可。

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define mset(x, y) memset(x, y, sizeof x)#define mcpy(x, y) memcpy(x, y, sizeof x)using namespace std;typedef long long LL;typedef pair <int, int> pii;inline int Read(){    int x = 0, f = 1, c = getchar();    for (; !isdigit(c); c = getchar())        if (c == '-')            f = -1;    for (;  isdigit(c); c = getchar())        x = x * 10 + c - '0';    return x * f;}const int MAXN = 200005;struct Edge{    int u, v;    LL w;    bool operator < (const Edge &b) const { return w < b.w; }};int n, a[MAXN], f[MAXN], siz[MAXN];vector <pii> adj[MAXN];vector <Edge> edg;vector <int> seq;bool vis[MAXN];LL d[MAXN];inline int Find(int x){    while (x ^ f[x])        x = f[x] = f[f[x]];    return x;}inline void FindRoot(int x, int p, int Siz, int &c){    bool f = true;    siz[x] = 1;    for (auto e : adj[x])        if (!vis[e.xx] && (e.xx ^ p))            FindRoot(e.xx, x, Siz, c), f &= siz[e.xx] <= Siz >> 1, siz[x] += siz[e.xx];    f &= siz[x] >= Siz >> 1;    if (f)        c = x;}inline void FindSize(int x, int p){    seq.pb(x), siz[x] = 1;    for (auto e : adj[x])        if (!vis[e.xx] && (e.xx ^ p))            d[e.xx] = d[x] + e.yy, FindSize(e.xx, x), siz[x] += siz[e.xx];}inline void Solve(int x, int Siz){    int c, p = 0;    FindRoot(x, 0, Siz, c);    vis[x = c] = true, d[x] = 0, seq.clear();    FindSize(x, 0);    LL val = 1LL << 60;    for (auto y : seq)        if ((d[y] += a[y]) < val)            val = d[y], p = y;    for (auto y : seq)        edg.pb({p, y, d[p] + d[y]});    for (auto e : adj[x])        if (!vis[e.xx])            Solve(e.xx, siz[e.xx]);}int main(){#ifdef wxh010910    freopen("data.in", "r", stdin);#endif    n = Read();    for (int i = 1; i <= n; i ++)        a[i] = Read();    for (int i = 1, x, y, w; i < n; i ++)        x = Read(), y = Read(), w = Read(), adj[x].pb(mp(y, w)), adj[y].pb(mp(x, w));    Solve(1, n);    sort(edg.begin(), edg.end());    for (int i = 1; i <= n; i ++)        f[i] = i;    LL ans = 0;    for (auto e : edg)        if (Find(e.u) ^ Find(e.v))            f[Find(e.u)] = Find(e.v), ans += e.w;    return printf("%lld\n", ans), 0;}
原创粉丝点击