2022 Hangzhou Normal U Summer Trials

发布时间 2023-05-06 10:45:02作者: Zeoy_kkk

Subarrays

给定一个长度为n的由正整数组成的序列,请你输出该序列中子段和能被\(k\)整除的所有符合要求的子段数量

题解:组合计数 + 前缀和 + 思维

\[sum[l,r]\ \ mod\ \ k = 0\\ (sum[r]-sum[l-1]) \ \ mod \ \ k = 0\\ sum[r] \ \ mod\ \ k \equiv sum[l-1]\ \ mod \ \ k \]

根据上面的推导我们可以得到:如果某个下标下的前缀和与前面某个下标下的前缀和在模\(k\)下同余,就说明两个下标之间的子段和能被\(k\)整除

所以我们只需要先计算一遍前缀和,然后对于每个下标下的前缀和利用map记录一下每个下标下的前缀和对\(k\)的模数即可,最后组合计数,比如说对\(k\)取模的前缀和状态一共有\(m\)个,那么我们只需要在这\(m\)个状态中任意取两个即可,即\(C_{m}^{2}\),其余的状态以此类推

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e5 + 10, M = 4e5 + 10;

int n, k;
int a[N];
int pre[N];
unordered_map<int, int> mp;

void solve()
{
    cin >> n >> k;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 1; i <= n; ++i)
    {
        pre[i] = (pre[i - 1] + a[i]) % k;
        mp[pre[i] % k]++;
    }
    int ans = 0;
    ans += mp[0];
    for (auto [x, y] : mp)
    {
        ans += (y * (y - 1)) / 2;
    }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Easy Problem

在一个\(n \times n\)地图上,包含了障碍*,玩家a,玩家b,你现在需要控制两个玩家同时上下左右移动,如果遇到障碍或者边界无法移动,请你计算出两个玩家能够遇到的最短时间,如果两个玩家无法遇到,输出no solution

\(2 \le n \le 50\)

题解:BFS

\(vis[ax][ay]][bx][by]\)代表玩家\(a\)从起点运动到\((ax,ay)\)并且玩家\(b\)从起点运动到\((bx,by)\)的最小步数

在BFS过程中,如果某个玩家遇到障碍或者边界,我们保持其坐标不变即可

最后当\((ax,ay)=(bx,by)\)时,说明两个玩家遇到了,我们直接退出广搜,输出最小步数

如果广搜结束两个玩家都没有相遇,说明无法相遇,直接输出no solution

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 50 + 5, M = 4e5 + 10;

int n;
char g[N][N];
int dis[N][N][N][N];
bool vis[N][N][N][N];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

struct node
{
    int ax, ay, bx, by;
};

void bfs(int ax, int ay, int bx, int by)
{
    queue<node> q;
    q.push({ax, ay, bx, by});
    dis[ax][ay][bx][by] = 0;
    vis[ax][ay][bx][by] = true;
    while (q.size())
    {
        node u = q.front();
        q.pop();
        if (u.ax == u.bx && u.ay == u.by)
        {
            cout << dis[u.ax][u.ay][u.bx][u.by] << endl;
            return;
        }
        for (int i = 0; i < 4; ++i)
        {

            int nax = u.ax + dir[i][0];
            int nay = u.ay + dir[i][1];
            int nbx = u.bx + dir[i][0];
            int nby = u.by + dir[i][1];
            if (nax < 1 || nax > n || nay < 1 || nay > n || g[nax][nay] == '*')
                nax = u.ax, nay = u.ay;
            if (nbx < 1 || nbx > n || nby < 1 || nby > n || g[nbx][nby] == '*')
                nbx = u.bx, nby = u.by;
            if (vis[nax][nay][nbx][nby] == true)
                continue;
            vis[nax][nay][nbx][nby] = true;
            dis[nax][nay][nbx][nby] = dis[u.ax][u.ay][u.bx][u.by] + 1;
            q.push({nax, nay, nbx, nby});
        }
    }
    cout << "no solution" << endl;
}

void solve()
{
    cin >> n;
    int ax, ay, bx, by;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
        {
            cin >> g[i][j];
            if (g[i][j] == 'a')
                ax = i, ay = j;
            if (g[i][j] == 'b')
                bx = i, by = j;
        }
    bfs(ax, ay, bx, by);
}
signed main(void)
{
    Zeoy;
    int T = 1;
    while (T--)
    {
        solve();
    }
    return 0;
}

Optimal Biking Strategy

一个人需要从起点到达距离其p距离远的终点,他可以选择步行或者骑车,路上有n个自行车停靠站,他只能在自行车停靠站上车或者下车

骑车需要花钱,1元允许骑行最多s米,也就是说如果两个自行车站距离为x,那么需要花费\(\lceil \frac{x}{s} \rceil\)

现在他有k元,他到终点最少的步行距离

\(1≤n≤10_6,1≤p≤10_9,1≤s≤10_9,1≤k≤5\)

\(a_i<a_j\)

题解:线性DP + 贪心 \(O(n*k^2 + nk*logn)\)

  • 状态表示:\(O(nk)\)

\(f[i][j]\)代表从起点到第\(i\)个自行车站,花了\(j\)元后的最小步行距离

  • 状态属性:\(MIN\)

  • 状态计算:按照从上一个站点到第\(i\)个站点花费了多少钱对集合进行划分 \(O(k)\)

  1. 不花钱,即从第\(i-1\)个站点步行到第\(i\)个站点:\(f[i-1][j] + a[i] - a[i-1]\)
  2. 1元,从距离站点\(i\)最远的可以用1元骑行到站点\(i\)的站点\(k\)骑行到站点\(i\)\(f[k][j-1]\)
  3. 2元,从距离站点\(i\)最远的可以用2元骑行到站点\(i\)的站点\(k\)骑行到站点\(i\)\(f[k][j-2]\)
  4. ......

注意我们需要提前预处理\(L[i][j]\):代表距离站点\(i\)最远的可以用\(j\)元骑行到站点\(i\)的站点,因为站点序列a递增,我们直接二分即可,预处理复杂度:\(O(nk*logn)\)

  • 状态初始:\(f[i][0] = a[i]\)

  • 答案呈现:\(min\sum(f[i][k] + p - a[n])\)

const int N = 1e6 + 10, M = 8;

int n, p, s, k;
int a[N];
int f[N][M];
int L[N][M];

void solve()
{
    cin >> n >> p >> s;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    cin >> k;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= k; ++j)
            f[i][j] = INF;
    for (int i = 1; i <= n; ++i)
        f[i][0] = a[i];
    for (int i = 1; i <= n; ++i)		//二分预处理
        for (int j = 1; j <= k; ++j)
            L[i][j] = lower_bound(a + 1, a + n + 1, a[i] - j * s) - a;
    for (int i = 1; i <= n; ++i)		//DP
            for (int j = 1; j <= k; ++j)
        {
            f[i][j] = min(f[i][j], f[i - 1][j] + a[i] - a[i - 1]);
            for (int m = 1; m <= j; ++m)
                f[i][j] = min(f[i][j], f[L[i][m]][j - m]);
        }
    int ans = p;
    for (int i = 1; i <= n; ++i)
        ans = min(ans, f[i][k] + p - a[i]);
    cout << ans << endl;
}

IHI's Magic String

现在给定一个空串,你可以对该串操作q次,每次操作可能是一下的情况:

  1. 将一个字符添加到字符串的末尾
  2. 删除字符串末尾的一个字符
  3. 将现在字符串中所有字符x替换成字符y

那么问你在经过题目给定的q次操作后,输出最后的字符串,如果最后字符串为空,输出The final string is empty

题解:思维 + 模拟 \(O(n)\)

我们思考操作\(3\)如果字符\(x\)先被字符\(y\)替换,然后字符\(y\)被字符\(z\)替换,那么实际上就等价于字符\(x\)被字符\(z\)替换,也就是操作3是存在覆盖性的,所以我们可以考虑从后往前

我们首先设定\(f[x]\)代表字符\(x\)最后在字符串中变成的字符,初始状态下\(f[x]=x\)

  • 对于每一个操作3给定的\(x\)\(y\),我们可以令\(f[x] = f[y]\)

  • 对于每一个操作1来说,我们模拟后发现,倒着做的时候我们需要在字符串前面添加字符\(f[x]\),但是问题是字符\(f[x]\)应不应该被添加,因为该字符有可能被操作2删除了,所以我们可以先按题意模拟,预处理出在每个操作1添加的字符是否被操作2删除

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

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e5 + 10, M = 4e5 + 10;

struct node
{
    int op;
    char x;
    char y;
};

int n;
node qry[N];
stack<int> stk;
bool st[N];

void solve()
{
    cin >> n;
    string s;
    for (int i = 1; i <= n; ++i)
    {
        int op;
        char x, y;
        cin >> op;
        if (op == 1)
        {
            cin >> x;
            qry[i].op = op;
            qry[i].x = x;
            s += x;
            stk.push(i);
        }
        else if (op == 2)
        {
            qry[i].op = op;
            if (s.size())
            {
                st[stk.top()] = true;
                stk.pop();
                s.pop_back();
            }
        }
        else
        {
            cin >> x >> y;
            qry[i].op = op;
            qry[i].x = x;
            qry[i].y = y;
        }
    }
    if (s.empty())
    {
        cout << "The final string is empty" << endl;
        return;
    }
    s = "";
    char f[26];
    for (int i = 0; i < 26; ++i)
        f[i] = char(i + 'a');
    int cnt = 0;
    for (int i = n; i >= 1; i--)
    {
        int op = qry[i].op;
        if (op == 3)
        {
            char x = qry[i].x, y = qry[i].y;
            f[x - 'a'] = f[y - 'a'];
        }
        else if (op == 1)
        {
            if (!st[i])
            {
                char x = qry[i].x;
                s = f[x - 'a'] + s;
            }
        }
    }
    cout << s << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}