2024.11.13 CW 模拟赛

题面 & 题解

T1

算法: 并查集

用并查集维护合并的过程, 每次合并时将能力值小的父亲定义为能力值大的父亲即可.

#include "iostream"

using namespace std;

namespace IO
{
    template <typename T>
    inline void read(T &x)
    {
        x = 0;
        bool f = 0;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = 1;
            ch = getchar();
        }

        while (isdigit(ch))
            x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
        x = (f ? -x : x);
        return;
    }

    inline void print(int x)
    {
        static int sta[65];
        int top = 0;
        do
        {
            sta[top++] = x % 10, x /= 10;
        } while (x);
        while (top)
            putchar(sta[--top] + 48);
        putchar('\n');
    }
}
using namespace IO;

const int N = 1e5 + 10;

int n, m;
int a[N];

inline void init()
{
    read(n);
    for (int i = 1; i <= n; ++i)
        read(a[i]);
    read(m);
    return;
}

namespace dsu
{

    int fa[N];

    inline void init()
    {
        for (int i = 1; i <= n; ++i)
            fa[i] = i;
        return;
    }

    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
}

inline void calculate()
{

    dsu::init();

    while (m--)
    {
        int x, y;
        read(x), read(y);

        int f1 = dsu::find(x), f2 = dsu::find(y);
        if (f1 == f2)
            print(f1);
        else
        {
            if (a[f1] < a[f2])
                swap(f1, f2);
            dsu::fa[f2] = f1;
            print(f1);
        }
    }

    return;
}

inline void solve()
{

    init();

    calculate();

    return;
}

int main()
{

#ifdef FILE_IO
    freopen("company.in", "r", stdin);
    freopen("company.out", "w", stdout);
#endif

    solve();

    return 0;
}

T2

算法: 分解因数, 字符串

我们对于一个字符串, 设其最短循环节长度为 \(x\), 每次跳 \(a\) 个.

将这个字符串无限复制, 再将每 \(a\) 个字符分成一段, 找到最短的一组循环节即我们想要的.

容易得到, 该循环节的长度为 \(\frac{\rm{lcm(a,x)}}{a}\).

最后再将所有字符串的循环节长度都取一个最小公倍数即可.

\(a\) 于题目中已经给出了, 考虑如何求 \(x\).

因为不会 \(kmp\), 所以考场上乱搞了一个.

发现 \(x\) 一定为该字符串长度的因数, 所以将字符串长度 \(m\) 进行因数分解 + 排序 (后面有用).
时间复杂度 \(\mathcal{O}(n(\sqrt m + \log \log m))\).

对于每一个字符串, \(\Theta(n)\) 判断每一个因数长度的循环节是否合法, 由于排过序, 找到第一个合法的后 \(break\) 掉即可.
这样时间复杂度 \(\mathcal{O}(nm\log m)\).

总时间复杂度 \(\mathcal{O}(n(\sqrt m + m \log m + \log \log m))\), 因为 \(1 \le m \le 10^5\), 可以通过.
当然, 用 \(kmp\) 可以做到 \(\mathcal{O}(nm)\).

#include "iostream"
#include "algorithm"
#include "cmath"
#include "string"

using namespace std;

typedef long long ll;

const int N = 11;

int n;
int a[N], l[N];
string s[N];

inline void init()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> i[a];
    for (int i = 1; i <= n; ++i)
        cin >> i[s], l[i] = s[i].size(), s[i] = " " + s[i];
    return;
}

basic_string<int> mx, t[N];

inline void calculate()
{
    for (int i = 1; i <= n; ++i)
    {
        int t_ = sqrt(l[i]);
        for (int j = 1; j <= t_; ++j)
        {
            if (!(l[i] % j))
            {
                if (j * j < l[i])
                    t[i].push_back(j), t[i].push_back(l[i] / j);
                else
                    t[i].push_back(j);
            }
        }
        sort(t[i].begin(), t[i].end());
    }
    return;
}

inline bool check(int k, int r)
{
    for (int i = 1; i <= r; ++i)
    {
        int nw = r + i;
        while (nw <= l[k])
        {
            if (s[k][nw] != s[k][i])
                return 0;
            nw += r;
        }
    }
    return 1;
}

inline ll lcm(ll x, ll y) { return x * y / __gcd(x, y); }

inline void solve()
{

    init();

    calculate();

    for (int i = 1; i <= n; ++i)
    {
        for (int j : t[i])
        {
            if (check(i, j))
            {
                mx.push_back(j);
                break;
            }
        }
    }

    ll ans = 1;
    for (int i = 0; i < mx.size(); ++i)
    {
        int x = lcm(a[i + 1], mx[i]) / a[i + 1];
        ans = lcm(ans, x);
    }

    cout << ans << '\n';

    return;
}

int main()
{

#ifdef FILE_IO
    freopen("tiger.in", "r", stdin);
    freopen("tiger.out", "w", stdout);
#endif

    cin.tie(nullptr)->ios::sync_with_stdio(false);

    solve();

    return 0;
}

T3

先给两个组合数中"常用"的组合公式:

\[\sum_{i=0}^n C^x_i = C^{x+1}_{n+1} \rightarrow \sum_{i=l}^r C^x_i = C^{x+1}_{r+1} - C^{x+1}_l \]

\[\sum_{i=0}^{n} C_{x+i}^i = C^n_{x+n+1} \rightarrow \sum_{i=l}^r C^i_{x+i} = C^r_{x+r+1} - C^{l-1}_x \]

可以用数归证明 (画图模拟一下也可).

  • Subtask 2
    对于 \(40\%\) 的数据, 即求一个点到一条线的路径个数.
    \(a=b_2-d_1, b=a_2-c_1, c=c_2-a_2\), 枚举线上每一个点, 由一式推导即可得出结论:

\[\sum_{i=0}^c C^a_{a+b+i} = C^{a+1}_{a+b+c+1} - C^{a+1}_{a+b} \]

  • Subtask 3
    该部分相当于求一个点到一个矩形的路径个数.
    同 Subtask2, 再设 \(d=d_2-b_2\), 枚举矩形的每一条线, 由 Subtask2 和二式推导即可得出:

\[\sum_{i=0}^d (C_{a+b+c+1+i}^{a+1+i} - C_{a+b+i}^{a+1+i}) = \sum_{i=0}^d C_{a+b+c+1+i}^{a+i+1} - \sum_{i=0}^d C^{a+1+i}_{a+b+i} \]

\[= (C_{a+b+c+d+2}^{a+d+1}-C_{a+b+c+1}^c) - (C_{a+b+d+1}^{a+d+1} - C^a_{a+b}) \]

#include "iostream"

using namespace std;

#define int long long

const int N = 1e6 + 10, mod = 998244353;

int a1, b1, c1, d1;
int a2, b2, c2, d2;
int jc[N];

inline void init()
{
    jc[0] = 1;
    for (int i = 1; i <= 1e6; ++i)
        jc[i] = (1ll * jc[i - 1] * i) % mod;
    return;
}

namespace Get
{
    inline int qpow(int x, int y)
    {
        int ans = 1;
        while (y)
        {
            if (y & 1)
                ans = (ans * x) % mod;
            x = (x * x) % mod;
            y >>= 1;
        }
        return ans;
    }
    inline int inverse(int x) { return qpow(x, mod - 2); }
}

inline int C(int up, int dn) { return (jc[dn] * Get::inverse((jc[up] * jc[dn - up]) % mod)) % mod; }

inline void in()
{
    cin >> a1 >> b1 >> c1 >> d1;
    cin >> a2 >> b2 >> c2 >> d2;
    return;
}

inline void calculate()
{

    int a = b2 - d1, b = a2 - c1, c = c2 - a2, d = d2 - b2, x = c1 - a1, y = d1 - b1;

    int ans;

    ans = ((C(a + d + y + 2, a + b + c + d + x + y + 4) - C(a + d + 1, a + b + c + d + x + 3) + mod) % mod - (C(a + d + y + 2, a + b + c + d + y + 3) - C(a + d + 1, a + b + c + d + 2) + mod) % mod - (C(a + y + 1, a + b + c + x + y + 3) - C(a, a + b + c + x + 2) + mod) % mod + mod) % mod +
          (C(a + y + 1, a + b + c + y + 2) - C(a, a + b + c + 1) + mod) % mod - (C(a + d + y + 2, a + b + d + x + y + 3) - C(a + d + 1, a + b + d + x + 2) + mod) % mod + (C(a + d + y + 2, a + b + d + y + 2) - C(a + d + 1, a + b + d + 1) + mod) % mod +
          (C(a + y + 1, a + b + x + y + 2) - C(a, a + b + x + 1) + mod) % mod - (C(a + y + 1, a + b + y + 1) - C(a, a + b) + mod) % mod;
    (ans += mod << 2) %= mod;

    cout << ans << '\n';

    return;
}

inline void solve()
{

    init();

    int T;
    cin >> T;

    while (T--)
    {
        in();
        calculate();
    }
    return;
}

signed main()
{

#ifdef FILE_IO
    freopen("rectangle.in", "r", stdin);
    freopen("rectangle.out", "w", stdout);
#endif

    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    solve();

    return 0;
}

T4

状压板子题

只需要注意最后统计答案的时候需要遍历所有可能的合法情况, 也就是不一定所有点都走完 (不然会挂20pts).

#include "iostream"
#include "bitset"

using namespace std;

namespace IO
{
    template <typename T>
    inline void read(T &x)
    {
        x = 0;
        bool f = 0;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = 1;
            ch = getchar();
        }

        while (isdigit(ch))
            x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
        x = (f ? -x : x);
        return;
    }

    inline void print(int x)
    {
        static short sta[65];
        int top = 0;
        do
        {
            sta[top++] = x % 10, x /= 10;
        } while (x);
        while (top)
            putchar(sta[--top] + 48);
        putchar('\n');
    }
}
using namespace IO;

const int N = 21;

int n;
short w[N][N];
bitset<N> t;
int nd[N], ed = 1;

inline void init()
{

    t.reset();

    read(n);
    for (short i = 1; i <= n; ++i)
        for (short j = 1; j <= n; ++j)
            read(w[i][j]);

    for (short k = 1; k <= n; ++k)
    {
        int op;
        read(op);
        if (op ^ 1)
        {
            t[k] = 1;
            short m;
            read(m);
            for (int i = 1; i <= m; ++i)
            {
                int x;
                read(x);
                nd[k] |= (1 << x - 1);
            }
            ed |= nd[k] | (1 << k - 1);
        }
    }
    return;
}

int f[N][1 << 20];

inline void calculate()
{

    for (int i = 1; i < (1 << n); ++i)
        for (short j = 1; j <= n; ++j)
            f[j][i] = 1e9;

    f[1][1] = 0;
    for (int i = 1; i < (1 << n); ++i)
    {
        for (short j = 1; j <= n; ++j)
        {
            if (!((i >> j - 1) & 1))
                continue;

            for (short k = 1; k <= n; ++k)
            {
                if (!(j ^ k))
                    continue;
                if ((i >> k - 1) & 1)
                    continue;
                if (t[k] and ((i & nd[k]) ^ nd[k]))
                    continue;
                f[k][i ^ (1 << k - 1)] = min(f[j][i] + w[j][k], f[k][i ^ (1 << k - 1)]);
            }
        }
    }

    int ans = 1e9;

    for (int s = ed; s < (1 << n); ++s){
        if ((s & ed) ^ ed) continue;
        for (int i = 1; i <= n; ++i) ans = min(ans, f[i][s] + w[i][1]);
    }
        
    print(ans);

    return;
}

inline void solve()
{

    init();

    calculate();

    return;
}

int main()
{

#ifdef FILE_IO
    freopen("treasure.in", "r", stdin);
    freopen("treasure.out", "w", stdout);
#endif

    solve();

    return 0;
}
posted @   Steven1013  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示