Toyota Programming Contest 2024#1(AtCoder Beginner Contest 337)

Toyota Programming Contest 2024#1(AtCoder Beginner Contest 337)

A - Scoreboard

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;

void solve()
{
    int n;
    cin >> n;
    int x = 0;
    int y = 0;
    for (int i = 1; i <= n; i++)
    {
        int a, b;
        cin >> a >> b;
        x += a;
        y += b;
    }
    int ans = x - y;
    if (ans > 0)
    {
        puts("Takahashi");
    }
    else if (ans < 0)
    {
        puts("Aoki");
    }
    else
    {
        puts("Draw");
    }
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

B - Extended ABC

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;

void solve()
{
    string s;
    cin >> s;
    int n = s.size();
    for (int i = 1; i < n; i++)
    {
        if (s[i] < s[i - 1])
        {
            puts("No");
            return;
        }
    }
    puts("Yes");
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

C - Lining Up 2

解题思路:

\(dfs\)一次即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;

void solve()
{
    int n;
    cin >> n;
    vector<vector<int>> adj(n + 1);
    int r = 0;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        if (x == -1)
        {
            r = i;
        }
        else
        {
            adj[x].push_back(i);
        }
    }
    auto dfs = [&](auto self, int u) -> void
    {
        cout << u << ' ';
        for (auto v : adj[u])
        {
            self(self, v);
        }
    };
    dfs(dfs, r);
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

D - Cheating Gomoku Narabe

解题思路:

记录时,\(o\)为1,其余为0.

\(a[i][j]:记录以第i行第j列为末尾,总长度为k的序列的区间和。\)

遍历时,\(x为-1\),其余为\(1\)。遍历过程中,长度为\(k\)的区间和刚好为\(k\)时,\(k - a[i][j]\)就是一种填补数量。

横着跑一次,竖着跑一次。

时间复杂度\(O(n)\).

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;

void solve()
{
    int h, w, k;
    cin >> h >> w >> k;
    vector<string> g(h + 1);
    for (int i = 1; i <= h; i++)
    {
        cin >> g[i];
        g[i] = ' ' + g[i];
    }
    vector<vector<int>> a(h + 1, vector<int>(w + 1));
    int cur = 0;
    for (int i = 1; i <= h; i++)
    {
        cur = 0;
        queue<int> q;
        for (int j = 1; j <= w; j++)
        {
            if (q.size() == k)
            {
                cur -= q.front();
                q.pop();
            }
            int x = 0;
            if (g[i][j] == 'o')
            {
                x = 1;
            }
            q.push(x);
            cur += x;
            if (q.size() == k)
            {
                // cout << i << ' ' << j << ' ' << cur << endl;
                a[i][j] = cur;
            }
        }
    }
    int ans = 1e9;
    for (int i = 1; i <= h; i++)
    {
        cur = 0;
        queue<int> q;
        for (int j = 1; j <= w; j++)
        {
            if (q.size() == k)
            {
                cur -= q.front();
                q.pop();
            }
            int x = 1;
            if (g[i][j] == 'x')
            {
                x = -1;
            }
            q.push(x);
            cur += x;
            if (q.size() == k)
            {
                if (cur == k)
                {
                    ans = min(k - a[i][j], ans);
                }
            }
        }
    }
    for (int i = 1; i <= w; i++)
    {
        cur = 0;
        queue<int> q;
        for (int j = 1; j <= h; j++)
        {
            if (q.size() == k)
            {
                cur -= q.front();
                q.pop();
            }
            int x = 0;
            if (g[j][i] == 'o')
            {
                x = 1;
            }
            q.push(x);
            cur += x;
            if (q.size() == k)
            {
                // cout << i << ' ' << j << ' ' << cur << endl;
                a[j][i] = cur;
            }
        }
    }
    for (int i = 1; i <= w; i++)
    {
        cur = 0;
        queue<int> q;
        for (int j = 1; j <= h; j++)
        {
            if (q.size() == k)
            {
                cur -= q.front();
                q.pop();
            }
            int x = 1;
            if (g[j][i] == 'x')
            {
                x = -1;
            }
            q.push(x);
            cur += x;
            if (q.size() == k)
            {
                if (cur == k)
                {
                    ans = min(k - a[j][i], ans);
                }
            }
        }
    }
    if (ans > k)
    {
        ans = -1;
    }
    cout << ans << endl;
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

E - Bad Juice

解题思路:

每个人第二天出问题或者没出问题对应二进制变化\(1或者0\)

所以有\(n\)杯果汁,我们就需要\(n\)中不同的二进制编码。每种唯一即可对应情况选择出坏的果汁。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;

void solve()
{
    int n;
    cin >> n;
    n--;
    int m = 0;
    for (int i = 30; i >= 0; i--)
    {
        if (n >> i & 1)
        {
            cout << i + 1 << endl;
            m = i + 1;
            break;
        }
    }
    vector<vector<int>> a(m + 10, vector<int>(0));
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < 30; j++)
        {
            if (i >> j & 1)
            {
                a[j + 1].push_back(i + 1);
            }
        }
    }
    for (int i = 1; i <= m; i++)
    {
        cout << a[i].size() << ' ';
        for (auto x : a[i])
        {
            cout << x << ' ';
        }
        if (a[i].size() == 0)
        {
            continue;
        }
        cout << endl;
    }
    cout.flush();
    string s;
    cin >> s;
    int ans = 1;
    for (int i = 0; i < s.size(); i++)
    {
        if (s[i] == '1')
        {
            ans += 1 << i;
        }
    }
    cout << ans << endl;
    cout.flush();
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

G - Tree Inversion

解题思路:

看到求\(f(1)...f(n)\),思路开始向换根\(dp\)尝试。

首先,我们如何求出以\(1\)为根节点的有根树\(f[u]\)

\(u \to v\)路径中\(w\)的数量,就是求\(u \to v\)路径中,有多少个数字严格大于\(v\)。也就是说,沿着路径记录下来的所有结点编号集合中,有多少个数字是大于\(v\)的。

这个我们可以用树状数组来实时求取,先查再插再删。

这样我们\(f[1]\)就求得了。

接下来思考换根。

下图中,红色为子树\(2\),蓝色为子树\(1\)

假设初始图如下:

换根后如下:


)

我们发现,换根后对于子树\(2\)来说,失去了结点\(1\)对于自己的贡献。所以,我们要得到子树\(2\)中结点编号小于\(1\)的结点个数\(a\)

对于子树\(1\)来说,增加了结点\(2\)对于自己的贡献,所以,我们要得到子树\(1\)中结点编号小于\(2\)的结点个数\(b\)

除上述两点外,整棵树并未有其他贡献,所以整体变化量为\(x = b - a\)

所以,\(f[2] = f[1] + x\)

对上述特例进行扩展。遍历过程中,结点\(1\)可看作父节点\(u\),结点\(2\)可看作子节点\(v\)

如何求得一棵子树中所有结点编号在某个数值区间中的出现个数?

这里我们选择主席树。(据说有启发式合并解法)

有了树链剖分思想,求得子树\(2\)中的情况很容易,因为\(dfs\)序连续地包括住了他。但是子树\(1\)如何操作呢?

由于我们是以结点\(1\)为根节点建立的主席树,所以图中的子树\(1\)\(dfs\)序上并不连续。

当然,这里我们可以选择将子树\(1\)分为两个区间进行查询:

ll x = query(rt[lf[v] - 1], rt[0], 1, n, 1, v) + query(rt[dfn], rt[rf[v]], 1, n, 1, v) - query(rt[rf[v]], rt[lf[v] - 1], 1, n, 1, u);

但其实还有较为简单地方法。

如果当前子树根节点编号为\(v\),那么整个图中编号小于等于\(v\)的结点数一共只有\(v\)个。所以,我们用\(v\)减去子树\(v\)中小于等于\(v\)的结点个数,得到的就是父节点子树中编号小于\(v\)的结点个数了。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
const int N = 2E5 + 10;
vector<vector<int>> adj(N);
int tr[N];
int n;
ll sum = 0;
int ls[N * 22];
int rs[N * 22];
int lf[N];
int rf[N];
int idx = 0;
int dfn = 0;
int t[N * 22];
int rt[N];
ll ans[N];
int lowbit(int x)
{
    return x & -x;
}

void insert(int x, int val)
{
    for (int i = x; i <= n; i += lowbit(i))
    {
        tr[i] += val;
    }
}

int query(int x)
{
    int res = 0;
    for (int i = x; i; i -= lowbit(i))
    {
        res += tr[i];
    }
    return res;
}

void update(int &u, int pre, int l, int r, int val)
{
    if (u == 0)
    {
        u = ++idx;
    }
    t[u] = t[pre] + 1;
    if (l == r)
    {
        return;
    }
    int mid = l + r >> 1;
    if (val <= mid)
    {
        rs[u] = rs[pre];
        update(ls[u], ls[pre], l, mid, val);
    }
    else
    {
        ls[u] = ls[pre];
        update(rs[u], rs[pre], mid + 1, r, val);
    }
}

int query(int &u, int pre, int l, int r, int tl, int tr)
{
    // cout << u << ' ' << l << ' ' << r << ' ' << t[u] << ' ' << t[pre] << endl;
    if (tl <= l && tr >= r)
    {
        return t[u] - t[pre];
    }
    int res = 0;
    int mid = l + r >> 1;
    if (tl <= mid)
    {
        res += query(ls[u], ls[pre], l, mid, tl, tr);
    }
    if (tr > mid)
    {
        res += query(rs[u], rs[pre], mid + 1, r, tl, tr);
    }
    return res;
}

void dfs1(int u, int fa)
{
    sum += query(n) - query(u);
    insert(u, 1);
    lf[u] = ++dfn;
    // cout << u << ' ' << dfn << ' ' << ls[u] << endl;
    update(rt[dfn], rt[dfn - 1], 1, n, u);
    for (auto v : adj[u])
    {
        if (v == fa)
        {
            continue;
        }
        dfs1(v, u);
    }
    insert(u, -1);
    rf[u] = dfn;
}

void dfs2(int u, int fa)
{
    ans[u] = sum;
    for (auto v : adj[u])
    {
        if (v == fa)
        {
            continue;
        }
        ll x = v - query(rt[rf[v]], rt[lf[v] - 1], 1, n, 1, v) - query(rt[rf[v]], rt[lf[v] - 1], 1, n, 1, u);
        // cout << u << ' ' << v << endl;
        // cout << v << ' ' << query(rt[rf[v]], rt[lf[v] - 1], 1, n, 1, v) << ' ' << query(rt[rf[v]], rt[lf[v] - 1], 1, n, 1, u) << endl;
        sum += x;
        dfs2(v, u);
        sum -= x;
    }
}

void solve()
{

    cin >> n;
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    dfs1(1, -1);
    // cout << 1 << endl;
    dfs2(1, -1);
    // cout << ls[2] << endl;
    // cout << rs[2] << ' ' << ls[2] << endl;
    // cout << query(rt[rf[2]], rt[lf[2] - 1], 1, n, 1, 1) << endl;
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i] << ' ';
    }
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}
posted @ 2024-01-21 22:58  value0  阅读(45)  评论(0)    收藏  举报