跳转至

ABC312(A-F)

A - Chord

判断字符串 \(S\) 是否等于 ACE, BDF, CEG, DFA, EGB, FAC, GBD 之一

直接判断

#include <iostream>

using namespace std;

int main()
{
    string s;
    cin >> s;
    if(s == "ACE" || s == "BDF" || 
    s == "CEG" || s == "DFA" || s == "EGB" || s == "FAC" || s == "GBD")
        puts("Yes");
    else
        puts("No");
    return 0;
}

B - TaK Code

有一个 \(N \times M\) 的矩阵,矩阵中只有 #. ,问:有多少个如下形状的 \(9 \times 9\) 子矩阵

###.?????
###.?????
###.?????
....?????
?????????
?????....
?????.###
?????.###
?????.###

其中 ? 可以是 #.

直接枚举即可

#include <iostream>

using namespace std;

const int maxn = 110;
int n, m;
char s[maxn][maxn];
bool check1(int r, int c)
{
    for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
            if(s[r][c] != '#')
                return 0;
    return 1;
}
bool check2(int r, int c)
{
    for(int i = 0; i < 4; i++)
        if(s[r-i][c] != '.' || s[r][c-i] != '.')
            return 0;
    return 1;
}
bool check3(int r, int c)
{
    for(int i = 0; i < 4; i++)
        if(s[r+i][c] != '.' || s[r][c+i] != '.')
            return 0;
    return 1;
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        scanf("%s", s[i]+1);
    for(int i = 1; i+8 <= n; i++)
        for(int j = 1; j+8 <= m; j++)
            if(check1(i, j) && check2(i+3, j+3) && check3(i+5, j+5) && check1(i+6, j+6))
                printf("%d %d\n", i, j);
    return 0;
}

C - Invisible Hand

\(N\) 个卖家和 \(M\) 个买家,每个卖家都有预期卖出价格 \(A_i\) ,当价格大于或等于 \(A_i\) 时卖家才愿意卖出。买个买家有预期买入价格 \(B_i\) ,当价格小于或等于 \(B_i\) 时买家才愿意买入。找出最小的数 \(X\) ,使得愿意卖出的人数大于等于愿意买入的人数。

二分,证明单调性:如果 \(X\) 的值调低,则卖家会变少,而卖家会变多,反之同理,因此满足单调性。

对买家和卖家的价格排序,然后二分,check的时候也二分就能知道有多少卖家和买家。

#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 2E5 + 10;
int a[maxn], b[maxn], n, m;

int check(int num)
{
    return lower_bound(a, a+n, num+1) - a >= b + m - lower_bound(b, b+m, num);
}

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    for(int i = 0; i < m; i++)
        scanf("%d", &b[i]);
    sort(a, a+n);
    sort(b, b+m);
    int l = min(a[0], b[0]) - 1, r = max(a[n-1], b[m-1]) + 1;
    while(l < r)
    {
        int x = (l + r) / 2;
        if(check(x))
            r = x;
        else
            l = x + 1;
    }
    cout << l << endl;
    return 0;
}

当然,如果推一下会发现答案一定是 \(A_i\)\(B_i + 1\),进一步可以参考 这个题解 ,将 \(A_i\)\(B_i + 1\) 合并然后排序直接输出 \(M-1\) 元素就可以了,时间复杂度 \(O((N+M)log(N+M))\)

N, M = map(int, input().split())
A = list(map(int, input().split()))
B = list(map(int, input().split()))
print(sorted(A + list(map(lambda x: x + 1, B)))[M - 1])

D - Count Bracket Sequences

给你一个长度为 \(N\) 的字符串 \(S\)\(S\)()? 组成,你需要将 ? 替换成 (),问:有多少种方案能够将 \(S\) 替换成括号匹配的字符串。答案 \(\bmod 998244353\)

数据范围:\(N ≤ 3000\)


经典括号匹配题,( 看成 1) 看成 -1 ,合法字符串相当于前缀和大于等于 0

定义 \(dp[i][j]\) :前 \(i\) 个数字使得前缀和为 \(j\) 的方案数。

转移:

\[ dp[i][j] = \left\{ \begin{matrix} dp[i-1][j-1], & s[i]\ ==\ ( \\ dp[i-1][j+1], & s[i]\ ==\ ) \\ dp[i-1][j-1] + dp[i-1][j+1], & s[i]\ ==\ ? \\ \end{matrix} \right. \]

时间复杂度 \(O(N^2)\)

#include <iostream>
#include <cstring>

using namespace std;

const int maxn = 3010;
const int mod = 998244353;
int n;
char s[maxn];
int dp[maxn][maxn];

int main()
{
    scanf("%s", s+1);
    n = strlen(s+1);
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++)
    {
        if(s[i] == '(')
        {
            for(int j = 1; j <= n; j++)
                dp[i][j] = (dp[i][j] + dp[i-1][j-1]) % mod;
        }
        else if(s[i] == ')')
        {
            for(int j = 0; j < n; j++)
                dp[i][j] = (dp[i][j] + dp[i-1][j+1]) % mod;
        }
        else
        {
            for(int j = 1; j <= n; j++)
                dp[i][j] = (dp[i][j] + dp[i-1][j-1]) % mod;
            for(int j = 0; j < n; j++)
                dp[i][j] = (dp[i][j] + dp[i-1][j+1]) % mod;
        }
    }
    cout << dp[n][0] << endl;
    return 0;
}

E - Tangency of Cuboids

在三维空间中,有 \(N\) 个 互不相交的长方体,如果两个长方体有一面相邻,则称这两个长方体相邻。输出每个长方体与其他长方体相邻的数量。

数据范围:

  • \(1 ≤ N ≤ 10^5\)

  • \(0 ≤ X, Y, Z ≤ 100\)

空间只有 100,分成多个 \(1\times1\times1\) 的小方块。由于长方体互不相交,所以一个小方块只能属于一个长方体,如果两个小方块相邻,则说明对应的长方体是相邻的。用一个 id[100][100][100] 记录小方块的归属,然后遍历判断就可以了。使用哈希表记录每个长方体和谁相邻。

#include <iostream>
#include <unordered_set>

using namespace std;

const int maxx = 110;
const int maxn = 1E5 + 10;
unordered_set<int> face[maxn];
int id[maxx][maxx][maxx];
int n;

int main()
{
    cin >> n;
    for(int i = 1, x1, y1, z1, x2, y2, z2; i <= n; i++)
    {
        cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
        for(int x = x1; x < x2; x++)
            for(int y = y1; y < y2; y++)
                for(int z = z1; z < z2; z++)
                    id[x][y][z] = i;
    }
    for(int x = 0; x < 100; x++)
        for(int y = 0; y < 100; y++)
            for(int z = 0; z < 100; z++)
                if(id[x][y][z])
                {
                    if(id[x+1][y][z] && id[x+1][y][z] != id[x][y][z])
                    {
                        face[id[x+1][y][z]].insert(id[x][y][z]);
                        face[id[x][y][z]].insert(id[x+1][y][z]);
                    }
                    if(id[x][y+1][z] && id[x][y+1][z] != id[x][y][z])
                    {
                        face[id[x][y+1][z]].insert(id[x][y][z]);
                        face[id[x][y][z]].insert(id[x][y+1][z]);
                    }
                    if(id[x][y][z+1] && id[x][y][z+1] != id[x][y][z])
                    {
                        face[id[x][y][z+1]].insert(id[x][y][z]);
                        face[id[x][y][z]].insert(id[x][y][z+1]);
                    }
                }
    for(int i = 1; i <= n; i++)
        cout << face[i].size() << endl;
    return 0;
}

F - Cans and Openers

\(N\) 个物品,每个物品都有一个 \(X_i\) 属性,物品有三种类型,

  • 易拉罐:获得之后可以直接打开,打开之后获得 \(X_i\) 的分数。
  • 密封罐:需要使用开罐器才能打开,打开之后获得 \(X_i\) 的分数。
  • 开罐器:用于打开密封罐,可以使用 \(X_i\) 次。

问:从中选择 \(M\) 个物品,最多能获得多少分数?

数据范围:\(1 ≤ M ≤ N ≤ 2 \times 10^5\)


显然,对于三种物品,在能选择的情况下,都是选择 \(X_i\) 越大的越好。

接下来,固定易拉罐的数量, 假设已经选择了 \(i\) 个分数最高的易拉罐,那么剩下的 \(M-i\) 个物品就是密封罐和开罐器,按照优先级一个一个选即可。

这样的话,第一层枚举 \(i\) 需要遍历 \(M\) 次,第二层循环选择剩下的 \(M-i\) 个物品也需要 \(M\) 次,这样肯定超时。不过,由于物品之间的优先级关系,可以用类似前缀和的东西加速:具体来说,开两个数组 sa[]sb[]sa[i] 表示选择 i 个易拉罐能获得的最高分数,由于分数越高越好,因此构造 sa[] 只需要类似前缀和的方式从高到低加起来就可以。 sb[j] 表示选择 j 个其余物品能获得最高的分数,构造 sb[] 的过程也很简单,手上没有开罐器就加一个,如果有开罐器,也是从密封罐当中选一个分数最高的加起来。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long LL;

int main()
{
    int n, m;
    cin >> n >> m;
    vector<LL> a, b, c;
    vector<LL> sa(n+1), sb(n+1);
    for(int i = 0, t, x; i < n; i++)
    {
        scanf("%d %d", &t, &x);
        if(t == 0)
            a.push_back(x);
        else if(t == 1)
            b.push_back(x);
        else
            c.push_back(x);
    }
    sort(a.begin(), a.end(), greater<LL>());
    sort(b.begin(), b.end());
    sort(c.begin(), c.end());
    for(int i = 0; i < n; i++)
        if(i < a.size())
            sa[i+1] = sa[i] + a[i];
        else
            sa[i+1] = sa[i];
    for(int r = 0, i = 0; i < n; i++)
    {
        if(b.empty())
            sb[i+1] = sb[i];
        else if(r == 0)
        {
            if(!c.empty())
            {
                r += c.back();
                c.pop_back();
            }
            sb[i+1] = sb[i];
        }
        else
        {
            sb[i+1] = sb[i] + b.back();
            b.pop_back();
            r--;
        }
    }
    LL ans = 0;
    for(int i = 0; i <= m; i++)
        ans = max(ans, sa[i] + sb[m-i]);
    cout << ans << endl;
    return 0;
}

G - Avoid Straight Line

有一棵 \(N\) 个节点的树,求出满足下列条件的三元组 \((i, j, k)\) 的数量:

  • \(1 ≤ i < j < k ≤ N\)
  • 树中不存在包含 \(i\)\(j\)\(k\) 的简单路径。

数据范围:\(N ≤ 2 \times 10^5\)

参考 这个题解 的思路

#include <iostream>
#include <cstdio>

using namespace std;

typedef long long LL;
const int maxn = 2E5 + 10;
const int maxm = 4E5 + 10;
int n, sz[maxn];
int head[maxn], ne[maxm], edge[maxm], idx = 1;
void add(int u, int v)
{
    edge[idx] = v;
    ne[idx] = head[u];
    head[u] = idx++;
}

void dfs(int u, int fa)
{
    sz[u] = 1;
    for(int i = head[u]; i; i = ne[i])
    {
        int v = edge[i];
        if(v == fa)
            continue;
        dfs(v, u);
        sz[u] += sz[v];
    }
}

LL ans;

void dfs2(int u, int fa)
{
    LL sum = 0;
    for(int i = head[u]; i; i = ne[i])
    {
        int v = edge[i];
        sum += sz[v];
    }
    for(int i = head[u]; i; i = ne[i])
    {
        int v = edge[i];
        sum -= sz[v];
        ans -= sz[v] * sum;
    }
    for(int i = head[u]; i; i = ne[i])
    {
        int v = edge[i];
        if(v == fa)
            continue;
        sz[u] -= sz[v];
        sz[v] += sz[u];
        dfs2(v, u);
        sz[v] -= sz[u];
        sz[u] += sz[v];
    }
}

int main()
{
    cin >> n;
    ans = n * (n - 1ll) * (n - 2) / 6;
    for(int m = n-1, u, v; m--; )
    {
        scanf("%d %d", &u, &v);
        add(u, v);
        add(v, u);
    }
    dfs(1, 0);
    dfs2(1, 0);
    cout << ans << endl;
    return 0;
}

最后更新: 2023-09-12
创建日期: 2023-09-12