CodeChef March Challenge 2017 题解

CC XENTASK

由于要交替选,所以要不第一个人选位置为奇数的数,第二个人选位置为偶数的数,要不第一个人选位置为偶数的数,第二个人选位置为奇数的数。取最小值即可。

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

int n, sum[2][2];

void solve() {
    memset(sum, 0, sizeof sum);
    scanf("%d", &n);
    for (int i = 0; i < 2; i ++)
        for (int j = 1; j <= n; j ++) {
            int x;
            scanf("%d", &x);
            sum[i][j & 1] += x; 
        }
    printf("%d\n", min(sum[0][0] + sum[1][1], sum[0][1] + sum[1][0]));
}

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve();
}

CC EXTRAN

假设原数列最小的数是l,最大的数是r。加入的数只有两种情况,一种是加入了一个在l到r,这个直接判断有没有重复出现的数即可。第二种是加入了一个小于l-1,或大于r+1的数,对于这种情况只需要存下读入数据中最小和最大的数,如果最小的数加1出现在了读入数据中,则最大的数是答案,否则最小的数就是答案。

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>

using namespace std;

const int Inf = 1e9 + 7;

int n, ans;
map<int,int> ref;

void solve(int id) {
    ans = 0;
    int mn = Inf, mx = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++) {
        int x;
        scanf("%d", &x);
        if (ref[x] == id) ans = x; else
            ref[x] = id;
        mn = min(mn, x);
        mx = max(mx, x);
    }
    if (ans) printf("%d\n", ans); else {
        if (ref[mn + 1] == id) printf("%d\n", mx); else
            printf("%d\n", mn);
    }
}

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve(i);
}

CC BANDMATR

其实可以交换任意多次就是让你把0不断加入到矩阵中,为了尽量使k下,不难发现,我们应该尽量矩阵的右上角和左下角放数字。每当k减1就会多出两行斜着的1需要加入0。我们可以斜着一行一行枚举,判断当前剩下的0是否够填满这些格子。如过可以,继续判断下一行,否则当前k已经是最优解。

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

int n;

void solve() {
    int num = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++) {
            int x;
            scanf("%d", &x);
            if (x == 0) num ++;
        }
    int ans = n - 1;
    for (int i = 1; i <= n - 1; i ++)
        if (num >= i * (i + 1)) ans --; else
            break;
    printf("%d\n", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve();
}

CC SCHEDULE

首先预处理出a[i]表示第i段连续相同的长度是多少。那么我们可以考虑二分答案lim,然后判断是否合法。对于连续一段相同的,考虑不改变最左边和最右边的数,使当前段不会影响到相邻的段。那么我们每隔lim个数就改变一个数字,假如要改变最后一个数字,就变成改变倒数第二个数字以保证不改变最右边的数,这样一段需要改变的数字就是a[i]/(lim+1),判断一个总共改变数字的个数与k的关系即可。但是当lim=1时不能保证不改变最左和最右的数,那么特判一下即可。

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 1e6 + 5;

int n, m, k, a[MAXN];
char s[MAXN];

bool chk(int lim) {
    int num = 0;
    for (int i = 1; i <= m; i ++)
        num += a[i] / (lim + 1);
    return num <= k;
}

bool chk() {
    int num = 0;
    for (int i = 1; i <= n; i ++)
        if (s[i] - '0' == (i & 1)) num ++;
    return num <= k || (n - num) <= k;
}

void solve() {
    scanf("%d%d", &n, &k);
    scanf("%s", s + 1);
    m = 1, a[1] = 1;
    for (int i = 1; i <= n; i ++) {
        if (s[i] == s[i - 1]) a[m] ++; else
            a[++ m] = 1;
    }
    if (chk()) {
        printf("1\n");
        return;
    }
    int l = 2, r = n, ans;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (chk(mid)) r = mid - 1, ans = mid; else
            l = mid + 1;
    }
    printf("%d\n", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve();
}

CC PSHTBRTH

由于矩阵大小只有4*4,所以可以预处理出所有矩阵的sg值。这个只需依次枚举每个矩阵,然后枚举删掉的矩阵,mex起来,复杂度是 O(21644) 。现在只要把询问的一段区间矩阵的sg值异或起来就是答案,这个可以用树状数组轻松维护。

#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

const int MAXS = (1 << 16);
const int MAXN = 1e5 + 5;

char s[10];
int sg[MAXS], flag[257], sta[4][4][4][4];
int n, m, a[MAXN], tr[MAXN];

void prepare() {
    for (int x1 = 0; x1 < 4; x1 ++)
        for (int x2 = x1; x2 < 4; x2 ++)
            for (int y1 = 0; y1 < 4; y1 ++)
                for (int y2 = y1; y2 < 4; y2 ++)
                    for (int x = x1; x <= x2; x ++)
                        for (int y = y1; y <= y2; y ++) 
                            sta[x1][y1][x2][y2] += (1 << (4 * x + y));
    sg[0] = 0;
    for (int s = 1; s < MAXS; s ++) {
        memset(flag, 0, sizeof flag);
        for (int x1 = 0; x1 < 4; x1 ++)
            for (int x2 = x1; x2 < 4; x2 ++)
                for (int y1 = 0; y1 < 4; y1 ++)
                    for (int y2 = y1; y2 < 4; y2 ++) {
                        int num = sta[x1][y1][x2][y2];
                        if ((s & num) == num)
                            flag[sg[s - num]] = 1;
                    }
        for (sg[s] = 0; flag[sg[s]]; sg[s] ++);
    }
}

int get() {
    int now = 0;
    for (int x = 0; x < 4; x ++) {
        scanf("%s", s);
        for (int y = 0; y < 4; y ++) 
            if (s[y] == '1')
                now += (1 << (x * 4 + y));  
    }
    return sg[now];
}

void modify(int s, int val) {
    for (; s <= n; s += (s & -s)) tr[s] ^= val;
}

int query(int s) {
    int ans = 0;
    for (; s; s -= (s & -s)) ans ^= tr[s];
    return ans;
}

void solve() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++) {
        a[i] = get();
        modify(i, a[i]);
    }
    for (int i = 1; i <= m; i ++) {
        int ord, l, r, x;
        scanf("%d", &ord);
        if (ord == 1) {
            scanf("%d%d", &l, &r);
            printf((query(r) ^ query(l - 1)) ? "Pishty\n" : "Lotsy\n");
        } else {
            scanf("%d", &x);
            modify(x, a[x]);
            a[x] = get();
            modify(x, a[x]);
        }
    }
    for (int i = 1; i <= n; i ++) modify(i, a[i]);
}

int main() {
    prepare();
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve();
}

CC FAVGAME

设状态f[i][j]表示以i为根的子树当第一天在j小时开始时的最少花多少天,g[i][j]表示以i为根的子树当第一天在j小时开始花多少天同时那一天最少用多少小时。这样对于每个节点,我们可以用一个数位dp转移,ff[s]表示当儿子完成情况的二进制状态为时,最少花费的天数,gg[s]当儿子完成情况的二进制状态为时花费最少天数的同时最小,那一天最少需要用多少小时。暴力转移一下,复杂度是 O(n2mmh) ,由于2^m肯定不满,所以是可以过的。

#include <cstring>
#include <cstdio>
#include <algorithm>
#define min(a, b) ((a < b) ? a : b)

using namespace std;

const int MAXN = 1e3 + 5, MAXM = (1 << 10) + 5;
const int Inf = 1e9;

int n, h, t[MAXN], son[MAXN][15], num[MAXN];
int f[MAXN][30], g[MAXN][30], ff[MAXM], gg[MAXM]; 

void dfs(int now) {
    for (int i = 1; i <= num[now]; i ++) dfs(son[now][i]);
    int m = (1 << num[now]) - 1;
    bool flag = 0;
    for (int tim = 0; tim < h; tim ++) {
        if (t[now] + tim <= h) ff[0] = 1, gg[0] = t[now] + tim; else
            ff[0] = 2, gg[0] = t[now], flag = 1;
        for (int s = 1; s <= m; s ++) {
            ff[s] = Inf;
            for (int i = 1; i <= num[now]; i ++) {
                if (!(s & (1 << (i - 1)))) continue;
                int ls = s ^ (1 << (i - 1)), nt = son[now][i];
                int nd = ff[ls] + f[nt][gg[ls]] - (gg[ls] < h);
                if (nd < ff[s]) {
                    ff[s] = nd;
                    gg[s] = g[nt][gg[ls]];
                } else if (nd == ff[s]) gg[s] = min(gg[s], g[nt][gg[ls]]);
            }
        }
        if (flag) {
            for (int i = tim; i < h; i ++)
                f[now][i] = ff[m], g[now][i] = gg[m];
            break;
        }
        f[now][tim] = ff[m], g[now][tim] = gg[m];
    }
    f[now][h] = f[now][0], g[now][h] = g[now][0];
}

void solve() {
    scanf("%d%d", &n, &h);
    for (int i = 1; i <= n; i ++) scanf("%d", &t[i]);
    for (int i = 1; i <= n; i ++) {
        scanf("%d", &num[i]);
        for (int j = 1; j <= num[i]; j ++) scanf("%d", &son[i][j]);
    }
    dfs(1);
    printf("%d\n", f[1][0]);
}

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve();
}

CC SUMDIS

把起始点i从右到左枚举,假如是暴力的做法,可以从i+1枚举到n,得出每个点的最短路。这样复杂度肯定是O(n^2)的。假如一个点在起始点为i+1,i+2和i+3时都可以通过一个点转移过来,那么这个点之后就都可以视为由这个点转移过来,这还是比较显然的。那么我们把这些点和它的转移点并查集起来,因为它们的路径大部分是相同的,所以只需把它到转移点的路径长度等信息放到转移点上一起考虑就可以了。复杂度,玄学…

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int MAXN = 1e5 + 5;
const int Inf = 1e9 + 7;

int n, num, d[MAXN], fr[MAXN][3], path[MAXN][4], nxt[MAXN], st, ed;
int tot[MAXN], disf[MAXN], dis[MAXN], fa[MAXN];
LL sub[MAXN];
bool flag[MAXN];

void get_useful() {
    while (flag[st]) st = nxt[st];
    d[num = 1] = ed = st;
    for (int now = nxt[st]; now; ed = now, now = nxt[now]) 
        if (!flag[now]) nxt[ed] = d[++ num] = now;
    nxt[ed] = 0;
    reverse(d + 1, d + num + 1);
    for (int i = 1; i <= num; i ++) dis[d[i]] = Inf;
}

int get(int now) {
    if (fa[now] == now) return now;
    int tmp = fa[now];
    fa[now] = get(fa[now]);
    disf[now] += disf[tmp];
    return fa[now];
}

void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= 3; i ++)
        for (int j = 1; j <= n - i; j ++) 
            scanf("%d", &path[j][i]);
    LL ans = 0;
    for (int i = 1; i <= n; i ++) {
        fa[i] = i, tot[i] = 1;
        dis[i] = disf[i] = sub[i] = nxt[i] = flag[i] = 0;
        for (int j = 0; j < 3; j ++) fr[i][j] = 0;
    }
    st = n;
    for (int i = n - 1; i; i --) {
        get_useful();
        int id = i % 3;
        for (int j = 1; j <= num; j ++) {
            int now = d[j];
            for (int k = max(i, now - 3); k <= now - 1; k ++) {
                int ds = dis[get(k)] + disf[k] + path[k][now - k];
                if (ds < dis[now]) {
                    dis[now] = ds;
                    fr[now][id] = k;
                } else if (ds == dis[now]) fr[now][id] = min(fr[now][id], k);
            }
            ans += 1ll * tot[now] * dis[now] + sub[now];
            int tmp = fr[now][0];
            if (!tmp || fr[now][1] != tmp || fr[now][2] != tmp) continue;
            int fx = get(tmp);
            fa[now] = fx, disf[now] = disf[tmp] + path[tmp][now - tmp];
            sub[fx] += sub[now] + 1ll * disf[now] * tot[now];
            tot[fx] += tot[now];
            flag[now] = 1;
        }
        nxt[ed] = i;
    }
    printf("%lld\n", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i ++) solve();
}

CC BEARTRAP

手玩题,首先可以手玩出一种方案,主要思想是我们可以通过一些点的摆放固定猫在后面几步的走向,然后提前造一个笼子来包住它。然后就是找一种好的打法。
这里写图片描述
这个图,可以用画图打开,然后用油漆桶手玩!!!!

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

struct Coor {
    int x, y;
    Coor (int a, int b) {x = a, y = b;}
    Coor () {}
};

const Coor fx[6] = {Coor(-1, 0), Coor(0, -1), Coor(1, -1), Coor(-1, 1), Coor(0, 1), Coor(1, 0)};

char s[100];
Coor cat[100], block[100];
bool ok;

Coor operator - (Coor a, Coor b) {return Coor(a.x - b.x, a.y - b.y);}
Coor operator + (Coor a, Coor b) {return Coor(a.x + b.x, a.y + b.y);}
Coor operator * (Coor a, int p) {return Coor(a.x * p, a.y * p);}
Coor operator / (Coor a, int p) {return Coor(a.x / p, a.y / p);}
bool operator == (Coor a, Coor b) {return a.x == b.x && a.y == b.y;}

bool chk_around(Coor a, Coor b) {
    for (int i = 0; i < 6; i ++) 
        if ((a + fx[i]) == b) return 1;
    return 0;
}

void solve() {
    ok = 0;
    for (int i = 1; i <= 11; i ++) {
        scanf("%s", s + 1);
        if (s[1] == 'W') return;
        scanf("%d%d", &cat[i].x, &cat[i].y);
        switch (i) {
            case 1: block[1] = cat[1] * 2;
            case 2: block[2] = cat[2] * 2;
            case 3: block[3] = block[2] + (cat[3] - cat[2]) * 3;
            case 4: block[4] = block[2] - cat[4] + block[3];
            case 5: block[5] = (cat[5] - cat[4]) * 3 + cat[5];
            case 6: {
                int around = (chk_around(cat[6], block[3])) ? 3 : 4;
                int far = (around == 3) ? 4 : 3;
                block[6] = block[far] - cat[6] + block[around];
            }
            case 7: block[7] = cat[7] + cat[2];
            case 8: block[8] = cat[8] - cat[7] + cat[8];
            case 9: block[9] = block[8] - block[7] + block[8];
            case 10: block[10] = cat[7];
            case 11: block[11] = cat[10];
        }
        printf("BLOCK %d %d\n", block[i].x, block[i].y);
        fflush(stdout);
    }
    scanf("%s", s);
}

int main() {
    int t, m;
    scanf("%d%d", &t, &m);
    for (int i = 1; i <= t; i ++) solve();
}

CC TUPLES2

直接把两部分的答案分别求出来想加,对于三条路径没交的情况,设状态f[i][j][k][s]表示做到i节点的子树,选了j条路径,k条伸出i,i是否已经被一条路覆盖,我们只需要讨论一下两条路径是否会在节点i相交变成一条,然后就是经典的树形dp了。对于三条路径必须有叫的情况。我们可以枚举深度最小的三条路径的交点x,然后枚举一下有多少条路径在x的子树内,多少条有部分在子树外,分别统计一下个数乘起来。

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 3e5 + 5;
const int Mo = 1e9 + 7;

int ans, n, all, inv[11], size[MAXN];
int f[MAXN][4][2][2], t[4][2][2];
int tot, Last[MAXN], Next[MAXN * 2], Go[MAXN * 2];

void link(int u, int v) {
    Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v; 
}

int C(int n, int m) {
    int ans = 1;
    for (int i = 0; i < m; i ++) ans = 1ll * ans * (n - i) % Mo;
    return 1ll * ans * inv[m] % Mo;
}

int pow(int x, int y) {
    int ans = 1;
    for (; y; y >>= 1, x = 1ll * x * x % Mo)
        if (y & 1) ans = 1ll * ans * x % Mo;
    return ans;
}

void dfs(int now, int pre) {
    f[now][0][0][0] = 1;
    f[now][0][1][0] = 1;
    for (int p = Last[now]; p; p = Next[p]) {
        int v = Go[p];
        if (v == pre) continue;
        dfs(v, now);
        memset(t, 0, sizeof t);
        for (int j1 = 3; j1 + 1; j1 --)
            for (int k1 = 0; k1 < 2; k1 ++)
                for (int l1 = 0; l1 < 2; l1 ++) {
                    if (!f[now][j1][k1][l1]) continue;
                    for (int j2 = 0; j2 < 4 - j1; j2 ++)
                        for (int k2 = 0; k2 < 2; k2 ++)
                            for (int l2 = 0; l2 < 2; l2 ++) {
                                if (!f[v][j2][k2][l2] || (k2 == 1 && l1)) continue;
                                int j3 = j1 + j2, k3 = k1 | k2, l3 = l1;
                                if (k1 == 1 && k2 == 1) j3 ++, k3 = 0, l3 = 1;
                                if (j3 < 4) (t[j3][k3][l3] += 1ll * f[now][j1][k1][l1] * f[v][j2][k2][l2] % Mo) %= Mo; 
                            }
                }
        memcpy(f[now], t, sizeof t);
    }
}

void dfs2(int now, int pre) {
    size[now] = 1; int all = 0;
    for (int p = Last[now]; p; p = Next[p]) {
        int v = Go[p];
        if (v == pre) continue;
        dfs2(v, now);
        all = (all + 1ll * size[v] * size[now] % Mo) % Mo;
        size[now] += size[v];
    }
    for (int i = 0; i < 3; i ++)
        ans = (ans + 1ll * C(1ll * size[now] * (n - size[now]) % Mo, i) * C(all, 3 - i) % Mo) % Mo;
}

int main() {
    inv[0] = 1;
    for (int i = 1; i <= 10; i ++) inv[i] = 1ll * inv[i - 1] * pow(i, Mo - 2) % Mo;
    scanf("%d", &n);
    for (int i = 1; i < n; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        link(u, v), link(v, u);
    }
    dfs(1, 0);
    ans = (f[1][3][0][0] + f[1][3][0][1]) % Mo;
    dfs2(1, 0);
    printf("%d\n", ans);
}

SORTROW

打了一个只交换同行的暴力,骗了1分…

#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int MAXN = 305;

struct Node {
    int num, s;
    Node(int a, int b) {num = a, s = b;}
    Node() {}
};

Node b[MAXN];
int n, a[MAXN][MAXN];
typedef long long LL;

bool cmp(Node a, Node b) {
    return a.num < b.num;
}

void solve() {
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j ++) b[j] = Node(a[i][j], j);
        sort(b + 1, b + n + 1, cmp);
        LL ans1 = 0, ans2 = 0;
        for (int j = 1; j <= n; j ++)
            ans1 = 1ll * (b[j].s - j) * (b[j].s - j);
        reverse(b + 1, b + n + 1);
        for (int j = 1; j <= n; j ++) 
            ans2 = 1ll * (b[j].s - j) * (b[j].s - j);
        if (ans1 < ans2) reverse(b + 1, b + n + 1);
        for (int j = 1; j <= n; j ++) printf("%d ", b[j].num);
        printf("\n");
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
            scanf("%d", &a[i][j]);
    solve();
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值