AtCoder Beginner Contest 209

AtCoder Beginner Contest 209

A - Counting

题意:

题解:

代码:

#include <bits/stdc++.h>

#define int long long
#define debug(x) cout << #x << " = " << x << endl;
using namespace std;

int const MAXN = 2e5 + 10;
int n, m, T, a[MAXN];

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n >> m;
    cout << max(m - n + 1, (int)0) ;
    return 0;
}

B - Can you buy them all?

题意:

题解:

代码:

#include <bits/stdc++.h>

#define int long long
#define debug(x) cout << #x << " = " << x << endl;
using namespace std;

int const MAXN = 2e5 + 10;
int n, m, T, a[MAXN];

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n >> m;
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (!(i & 1)) a[i] -= 1;
        sum += a[i];
    }
    if (sum <= m) puts("Yes");
    else puts("No");
    return 0;
}

C - Not Equal

题意:

题解:

代码:

#include <bits/stdc++.h>

#define int long long
#define debug(x) cout << #x << " = " << x << endl;
using namespace std;

int const MAXN = 2e5 + 10;
int n, m, T, a[MAXN], mod = 1e9 + 7;

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    sort(a + 1, a + 1 + n);
    int res = 1;
    for (int i = 1; i <= n; ++i) {
        res = (res * max((a[i] - i + 1), (int)0)) % mod;
    }
    cout << res;
    return 0;
}

D - Collision

题意: 一颗n个点的树,有q个询问,每次询问2个点a和b之间的距离的奇偶性。如果距离是奇数,打印road;否则,打印Town。 2 < = N < = 1 0 5 , 1 < = Q < = 1 0 5 2<=N<=10^5,1<=Q<=10^5 2<=N<=105,1<=Q<=105

题解:

法一:询问距离,直接lca,然后判断奇偶性

法二:因为只需要奇偶性,因此可以化简lca的过程。查看使用lca求距离的公式:dist[a] + dist[b] - 2 * dist[lca(a, b)],既然是模2,那么直接就算(dist[a] + dist[b]) % 2,所以其实我只需要算出(dist[a] + dist[b])即可,这样就跳过lca的步骤。

代码:

// lca版本
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;
int f[N][20], d[N], dist[N];  // f[i][j]表示从i开始,往上走2^j步到达的点,d为深度,dist为距离
int e[N], ne[N], h[N], idx, w[N];
int T, n, m, t;  // t为数的深度
queue<int> q;

void add(int a, int b, int c) {
    e[idx] = b,  w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

// 预处理:得到每个点的深度,距离,f数组
void bfs() {
    q.push(1);  // 把根放入队列,注意这里有可能根不是1
    d[1] = 1;
    while (q.size()) {
        int x = q.front();
        q.pop();
        for (int i = h[x]; i != -1; i = ne[i]) {
            int y = e[i];
            if (d[y]) continue;
            d[y] = d[x] + 1;  // 更新深度
            dist[y] = dist[x] + w[i];  // 更新距离
            
            // 进行dp更新
            f[y][0] = x;
            for (int j = 1; j <= t; ++j) {
                f[y][j] = f[f[y][j - 1]][j - 1];  // 分两段处理
            }
            q.push(y);
        }
    }
}

// 查找x和y的最近公共祖先
int lca(int x, int y) {
    if (d[x] > d[y]) swap(x, y);  // 保证x的深度浅一点
    for (int i = t; i >= 0; --i)
        if (d[f[y][i]] >= d[x]) y = f[y][i];  // 让x和y到同一个深度
    if (x == y) return x;
    for (int i = t; i >= 0; --i) {// 让x和y之差一步就能相遇
        if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    }
    return f[x][0];
}

int main() {
    //  cin >> T;
    T = 1;
     while (T--) {
         memset(h, -1, sizeof h);
         idx = t = 0;
         cin >> n >> m;
         t = (int)(log(n) / log(2)) + 1; // 得到树的深度
         
         // 读入一棵树
         for (int i = 0; i < n - 1; ++i) {
             int a, b, c;
             scanf("%d %d", &a, &b);
             add(a, b, 1), add(b, a, 1);
         }
        
        bfs();
        // 回答询问
        for (int i = 1; i <= m; ++i) {
            int a, b;
            scanf("%d %d", &a, &b);
            int cur_dist = dist[a] + dist[b] - 2 * dist[lca(a, b)];
            if (cur_dist & 1) puts("Road");
            else puts("Town");
        }
     }
    return 0;
}
// 模2版本
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;
int d[N];  // f[i][j]表示从i开始,往上走2^j步到达的点,d为深度,dist为距离
int e[N], ne[N], h[N], idx, w[N];
int T, n, m, t;  // t为数的深度
queue<int> q;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// 预处理:得到每个点的深度,距离,f数组
void bfs() {
    q.push(1);  // 把根放入队列,注意这里有可能根不是1
    d[1] = 1;
    while (q.size()) {
        int x = q.front();
        q.pop();
        for (int i = h[x]; i != -1; i = ne[i]) {
            int y = e[i];
            if (d[y]) continue;
            d[y] = d[x] + 1;  // 更新深度
            
            q.push(y);
        }
    }
}

int main() {
    //  cin >> T;
    T = 1;
     while (T--) {
         memset(h, -1, sizeof h);
         idx = t = 0;
         cin >> n >> m;
         t = (int)(log(n) / log(2)) + 1; // 得到树的深度
         
         // 读入一棵树
         for (int i = 0; i < n - 1; ++i) {
             int a, b, c;
             scanf("%d %d", &a, &b);
             add(a, b), add(b, a);
         }
        
        bfs();
        // 回答询问
        for (int i = 1; i <= m; ++i) {
            int a, b;
            scanf("%d %d", &a, &b);
            int cur_dist = (d[a] + d[b]) % 2;
            if (cur_dist & 1) puts("Road");
            else puts("Town");
        }
     }
    return 0;
}

E - Shiritori

题意: 两个人玩成语接龙,如果一个人选择了单词A,那么下一个人只能选择单词B,同时保证单词A的最后三个字符和单词B的头三个字符相同,如果当前人无法继续接单词则失败。现在给定n个单词,每个单词长度在5 ~ 8之间,每次玩接龙只能在给定的单词内选择使用,同一个单词可以被多次使用。现在要求打印i个结果,问使用第i个单词开始进行接龙游戏,是先手必胜还是先手必败,还是平局。如果先手必胜,打印Aoki;先手必败,打印Takahashi;平局打印Draw。 1 < = n < = 2 e 5 1<=n<=2e5 1<=n<=2e5

题解: 拓扑排序。首先明确一个点,如果一个单词后面无法续上其他单词,那么这个单词就是必败态;如果一个单词后面存在必败态,那么这个单词就是必胜态;如果一个单词的后继状态全是必胜态,那么这个单词也是必败态。

因此,我们可以首先找出必败态单词作为初始起点,倒着找它的前驱去进行更新。即找到出度为0的点作为初始状态,这个看起来非常像拓扑排序(拓扑排序是找入度为0的点作为初始状态)。后面更新就按照类似拓扑排序的方式进行即可。

但是这个题目存在平局,怎么处理这个问题?我们只需要保证队列里面只放置必胜态或者必败态的点,那么每次更新都是拿必胜态或者必败态去更新,这样使得deg[u]–只可能是必败态或者必胜态。如果一个点的deg[u]为0,且还不是必胜态,那么它必然是必败态。

最后打印的时候,如果一个点是必败态,就打印先手必败;是必胜态,就打印先手必胜;如果都不是,那就是平局。

同时还需要考虑一个建图问题,如果暴力建图就是 O ( n 2 ) O(n^2) O(n2),因此需要优化,每三个字符就优化为一个点,然后每个单词都优化为一条边,这样建图就是 O ( n ) O(n) O(n),这种建图方式类似这题:https://www.acwing.com/problem/content/1167/

代码:

#include <bits/stdc++.h>
using namespace std;

int const MAXN = 4e5 + 10;

int n, idx, deg[MAXN];
char str[MAXN][10];
bool win[MAXN], fail[MAXN];
queue<int> q;
vector<int> from[MAXN];
map<int, int> mp;

int Hash(char s1, char s2, char s3) {
    int tmp = (s1 - 'A' + 1) * 100 * 100 + (s2 - 'A' + 1) * 100 + (s3 - 'A' + 1);
    if (!mp[tmp]) mp[tmp] = ++idx;
    return mp[tmp];
}

signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%s", str[i] + 1);
        int len = strlen(str[i] + 1);
        int u = Hash(str[i][1], str[i][2], str[i][3]), v = Hash(str[i][len - 2], str[i][len - 1], str[i][len]);
        from[v].push_back(u);  // 记录前驱
        deg[u]++;  // 出度
    }
    for (int i = 1; i <= idx; i++)
        if (!deg[i]) fail[i] = 1, q.push(i);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 0; i < from[u].size(); i++) {
            int v = from[u][i];  // 找前驱
            deg[v]--;
            if (fail[u] && !win[v]) win[v] = 1, q.push(v);  // 后继是必败他,那么就是必胜态
            if (!deg[v] && !win[v]) fail[v] = 1, q.push(v);  // 后继全部更新完,还不是必胜态,那么就是必败态
        }
    }
    for (int i = 1; i <= n; i++) {
        int len = strlen(str[i] + 1);
        int v = Hash(str[i][len - 2], str[i][len - 1], str[i][len]);
        if (win[v]) puts("Aoki");
        else if (fail[v]) puts("Takahashi");
        else puts("Draw");
    }
    return 0;
}

F - Deforestation

题意: 砍树。

题解: dp。想不明白别人的转移,鸽了趴,哪天半夜睡不着再来看看。

代码:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值