XJOI模拟训练22

Posted bryane

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XJOI模拟训练22相关的知识,希望对你有一定的参考价值。

A.扫雷(minesweeper)

题目描述 :

你需要编写一个扫雷交互器,获取地图信息,依次读入玩家操作并返 回对应结果。
扫雷的局面是一个 n × m 的矩形,其中一些位置为地雷而另一些位置 为空地,扫雷局面将以字符矩阵的形式输入。将第 i 行第 j 列的位置记作 ?i, j?。特别地,令 k 为地雷的数量,保证有 0 < k < n × m。
一开始玩家无法得知除了 n, m, k 之外的扫雷局面的任何信息。
你需要维护名为玩家地图的字符矩阵,初始时矩阵中所有元素为 _(下划线)。

玩家将进行 q 次操作,每次将选取一个位置 (?x, y?),并用以下三种 方式之一点击(若游戏结束,你应该忽略游戏结束后的所有玩家操作,即判 定这些操作为无效操作,反之即为有效操作):

  1. 左键点击: 若 (?x, y?) 已经被打开或玩家地图中这个位置为 P(P 表示旗子),则不 进行任何操作;否则若 (?x, y?) 为地雷,则游戏失败;否则对 (?x, y?) 进 行打开操作。
  2. 右键点击: 若 (?x, y?) 已经被打开,则不进行任何操作;否则 若玩家地图中位置 (?x, y?) 为 **_**(下划线),将其改为 P, 若玩家地图中位置 (?x, y?) 为 P,将其改为 ?, 若玩家地图中位置 (?x, y?) 为 ?,将其改为 _。
  3. 中键点击: 若 (?x, y?) 未被打开,或玩家地图中这个位置周围相邻的 8 个位置的 P 的个数不等于玩家地图中该位置的数值,则不进行任何操作;否则对 于 (?x, y?) 周围相邻 8 个未被打开且在玩家地图中不是 P 的位置,如果 存在至少一个位置是地雷,则游戏失败;否则对这些位置进行打开操 作。

打开操作: 对位置 (?x, y?) 进行的打开操作按照如下方式进行:

  1. (?x, y?) 标记为被打开。
  2. 在玩家地图中位置 (?x, y?) 改为 c(0 ≤ c ≤ 8),表示这个位置周围相邻 的 8 个位置的地雷数量。
  3. 如果 c = 0,则对其周围相邻的 8 个未被打开且在玩家地图中不是 P 的位置进行打开操作。
  4. 这个操作是递归进行的,直到所有子操作都结束后,本次打开操作才 算结束。

游戏结束,游戏结束有以下三种情况:

  1. 游戏失败:即上述规则中的情况,试图对为地雷的位置进行打开操作 就会导致游戏失败。
  2. 游戏胜利:若某一次操作结束后,未被打开的位置个数恰好为 k,则 此次操作后游戏胜利。
  3. 退出游戏:若玩家操作结束,但上述两种情况均未出现,则视作玩家 退出游戏。

在每一次操作后,你需要返回结果,具体规则如下:

  • 若此次操作为无效操作,返回 INVALID;否则
  • 若此次操作后游戏失败,返回 LOSE;否则
  • 先返回 RUNNING:,然后在同一行返回用中括号包含的,玩家地图中 有更改的位置以及更改后的值,格式为 <x, y, val> ;更改的位置 按照 x 坐标为第一关键字从小到大,y 坐标为第二关键字从小到大 的顺序排序,相邻两个更改的位置用 , 隔开。例如某一次操作后可能 返回:RUNNING: [];或 RUNNING: [<3, 3, ?>]。注意其中空格的位置。
  • 若此次操作后游戏胜利,再在新的一行返回 WIN。
  • 若此次操作后退出游戏,再在新的一行返回 QUIT。

输入格式 :

输入文件包含多组数据。
第一行一个正整数 T 表示数据组数,接下来每 n + q + 2 行(意义见 下)表示一组数据。
每组数据第一行两个正整数 n, m 分别表示扫雷局面的高度和宽度。
每组数据接下来 n 行,第 i 行一个长度为 m 的字符串,仅包含 _ 和 (*) 两种字符。
如果第 j 个字符为 *,则表示第 i 行第 j 列为地雷,否则为空 地。
每组数据接下来若干行,每行三个正整数 op, x, y 表示玩家的一次操作, 具体操作见题目描述。
每组数据最后一行一个数 0,表示玩家操作结束。令玩家操作次数为 q。

输出格式 :

对于每组数据,输出若干行,每行表示一次操作的返回结果,若在某一 次操作后游戏结束,请输出对应的结果。
相邻的两组数据之间使用一行 (==========)(10 个 = 字符,不包含引 号)隔开。

数据范围 :

对于所有测试点:1 ≤ T ≤ 30,3 ≤ n, m ≤ 200,0 ≤ q ≤ 10000, op ∈ {1, 2, 3},1 ≤ x ≤ n,1 ≤ y ≤ m;

Solution :

很少做的一道模拟好题,
题目各种情况其实已经描述得很清楚了,按题意大力模拟就好了。
对无效操作的定义没有看清楚导致爆零 =w=

Code :

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define fr(i, a, b) for(int (i) = (a); (i) <= (b); (i)++)
typedef pair<int, int> pii;
typedef pair<pii, int> pli;
const int N = 205;
int n, m, T, close;
char s[N][N], a[N][N];
vector<pli> prt;
template <class T> void read(T &x) {
    bool f = false; x = 0;
    char ch = getchar();
    while (ch<'0' || ch>'9') {if (ch == '-') f = true; ch = getchar();}
    while (ch>='0' && ch<='9') x = x * 10 + ch - '0', ch = getchar();
    if (f) x = -x;
}
inline void open(int x, int y) {
    close --;
    int cnt = 0;
    fr(i, x - 1, x + 1) fr(j, y - 1, y + 1) {
        if (i < 1 || i > n || j < 1 || j > m) continue;
        if (a[i][j] == '*') cnt++;
    }
    prt.pb(mp(mp(x, y), cnt));
    s[x][y] = '0' + cnt;
    if (cnt == 0) {
        fr(i, x - 1, x + 1) fr(j, y - 1, y + 1) {
            if (i < 1 || i > n || j < 1 || j > m) continue;
            if (s[i][j] == '_' || s[i][j] == '?') {
                open(i, j);
            }
        }
    }
}
inline void shuchu() {
    sort(prt.begin(), prt.end());
    int sz = prt.size() - 1;
    printf("RUNNING: [");
    fr(i, 0, sz) {
        printf("<%d, %d, %d>", prt[i].fi.fi, prt[i].fi.se, prt[i].se);
        if (i != sz) printf(", ");
    }
    puts("]");
}
int main() {
    read(T);
    while (T--) {
        read(n), read(m);
        int mine = 0;
        fr(i, 1, n) {
            scanf("%s", a[i] + 1);
            fr(j, 1, m) if (a[i][j] == '*') mine++;
        }
        fr(i, 1, n) fr(j, 1, m) s[i][j] = '_';
        int op, x, y; close = n * m;
        bool lose = false, win = false;
        while (~scanf("%d", &op)) {
            if (op == 0) {
                if (!win && !lose) puts("QUIT");
                break;
            }
            read(x), read(y);
            if (lose || win) {puts("INVALID"); continue;}
            if (op == 1) {
                if (s[x][y] >= '0' && s[x][y] <= '9' || s[x][y] == 'P')  {puts("RUNNING: []"); continue;}
                if (a[x][y] == '*') {
                    puts("LOSE"); lose = true;
                    continue;
                }
                prt.clear();
                open(x, y); shuchu();
                if (close == mine) {puts("WIN"); win = true; continue;}
            }
            if (op == 2) {
                if (s[x][y] >= '0' && s[x][y] <= '9')  {puts("RUNNING: []"); continue;}
                if (s[x][y] == '_') s[x][y] = 'P';
                else if (s[x][y] == 'P') s[x][y] = '?';
                     else s[x][y] = '_';
                printf("RUNNING: [<%d, %d, %c>]
", x, y, s[x][y]);
            }
            if (op == 3) {
                if (!(s[x][y] >= '0' && s[x][y] <= '9')) {puts("RUNNING: []"); continue;}
                int cnt = 0;
                fr(i, x - 1, x + 1) fr(j, y - 1, y + 1) {
                    if (i < 1 || i > n || j < 1 || j > m) continue;
                    if (s[i][j] == 'P') cnt++;
                }
                if (cnt != (s[x][y] - '0')) {puts("RUNNING: []"); continue;}
                prt.clear();
                fr(i, x - 1, x + 1) {
                    fr(j, y - 1, y + 1) {
                        if (i < 1 || i > n || j < 1 || j > m) continue;
                        if (s[i][j] == 'P' || (s[i][j] >= '0' && s[i][j] <= '9')) continue;
                        if (a[i][j] == '*') {
                            lose = true;
                        } else open(i, j);
                    }
                }
                if (lose) {puts("LOSE"); continue;}
                shuchu();
                if (close == mine) {puts("WIN"); win = true; continue;}
            }
        }
        if (T) puts("==========");
    }
    return 0;
}

B.五彩树 (Colorful)

题目描述 :

给定一棵 (n) 个节点的树,节点编号为 (1 ~ n)。 每个节点都染上了一种颜色,总共有 (m) 种不同的颜色,编号为 (1 ~ m)。 记节点 (i) 的颜色为 (c_i)。 小 X 喜欢颜色,他想要选出这棵树的一个连通的部分 (S),并且 (S) 中的 节点必须包含至少 (k) 种不同的颜色。即 (S) 必须满足其是原树的一个连通的 导出子图,并且集合 ({c_u | u ∈ S}) 的大小至少为 (k)。 但是小 Y 讨厌颜色,她让小 X 把除了 (S) 中的节点之外的所有节点的 颜色都擦除,并且呆在一个节点 (u),她想要最大化节点 (u) 与任意的有颜色 的节点之间的距离的最小值。即最大化 (min {dis(u, x)} x ∈ S)(dis(x, y)) 表示节点 (x) 与节点 (y) 之间的距离,这里距离定义为最少经过的边数。 小 X 还没有确定选择 (S) 的方案,他求助于你,你需要满足上述所有条 件,并且最大化 (u)(S) 中的节点的距离。

输入格式 :

第一行三个正整数 (n, m, k),意义见题目描述。
第二行 (n) 个正整数 (c1...n) 依次表示每个节点的颜色。
接下来 (n ? 1) 行,每行两个正整数 (x, y) 表示一条连接节点 (x) 与节点 (y) 的边。

输出格式 :

输出一行一个数表示最大距离。

样例 :

输入

7 2 2
1 2 2 2 2 1 1
1 2
1 3
1 4
3 5
3 6
6 7

输出

3

数据范围 :

对于所有数据,(1 ≤ k ≤ m ≤ n ≤ 10^6)(1 ≤ ci ≤ m)

Solution :

该树是一棵无根树,我们钦定 1号点 为根结点,贪心的想,最后选出来的子图 (S) 有两种情况

  1. 是以 1 为根的树的子树
  2. 是以 1 为根的树删去一个子树

对于第一种情况,dis的最小值可以dp求出来
对于第二种情况,直接dfs找最大深度即可
这样我们能求出对于一个点作为 子树的根 的两种情况分别得到的dis值
接下来统计以 某一点为根的子树 内的和不包含这个子树的 颜色种类
考虑以某一点为根的子树dfs序是连续的,将dfs复制一份接在后面,则删去某一点为根的子树后剩下的部分在dfs序中也是连续的,于是我们就可以直接在dfs序上用双指针处理出每一点 达到 k 种颜色最早的位置,判断该位置是否包含在该点子树内即可。

dfs序真是个好东西 =w=

Code :

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7, inf = 1e9 + 7;
int n, m, k, cnt = 0, inx = 0;
int c[N], ct[N], num[N * 2], in[N], out[N];
int fir[N], nxt[N * 2], to[N * 2], dis[N], Mx[N], pos[N * 2];
inline void AddEdge(int u, int v) {
    nxt[++cnt] = fir[u];
    fir[u] = cnt, to[cnt] = v;
}
void dfs1(int u, int fa) {
    in[u] = ++inx;
    num[inx] = u;
    for (int i = fir[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == fa) continue;
        dfs1(v, u);
        dis[u] = max(dis[u], dis[v] + 1);
    }
    out[u] = inx;
}
void dfs2(int u, int fa) {
    int mx = 0, cmx = 0;
    for (int i = fir[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == fa) continue;
        if (dis[v] + 1 >= mx) cmx = mx, mx = dis[v] + 1;
        else cmx = max(cmx, dis[v] + 1);
    }
    for (int i = fir[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == fa) continue;
        Mx[v] = max(Mx[u] + 1, (dis[v] + 1 == mx) ? cmx + 1 : mx + 1);
        dfs2(v, u);
    }
}
int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
    for (int i = 1; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        AddEdge(x, y);
        AddEdge(y, x);
    }
    dfs1(1, 0);
    dfs2(1, 0);
    for (int i = 1; i <= n; i++) num[i + n] = num[i];
    int nw = 0;
    for (int i = 1, j = 1; j <= 2 * n; j++) {
        if (ct[c[num[j]]] == 0) nw ++;
        ct[c[num[j]]] ++;
        while (nw > k || ct[c[num[i]]] > 1) {
            ct[c[num[i]]] --;
            if (ct[c[num[i]]] == 0) nw --;
            i ++;
        }
        if (nw >= k) pos[j] = i;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (pos[out[i]] >= in[i]) ans = max(ans, Mx[i]);
        if (pos[in[i] + n - 1] > out[i]) ans = max(ans, dis[i] + 1);
    }
    printf("%d
", ans);
    return 0;
}

以上是关于XJOI模拟训练22的主要内容,如果未能解决你的问题,请参考以下文章

XJOI contest 1592

XJOI NOIP模拟题1

XJOI2016提高组模拟题一 排队

XJOI2018提高组训训练23

XJOI网上同步训练DAY6 T2

xjoi2016模拟题1