2024CCPC东北四省赛暨全国邀请赛VP题解

要是我还是大一新生就好了,这样就不会场场自闭了(

J.Breakfast

从未见识过的题目,简单到发昏,甚至没看见输入是固定的,直接计算即可。

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int mod = 1e9 + 7;

void solve()
{
    int n,m;
    cin >> n >> m;
    double res = 0.6 * n + m;
    cout << fixed << setprecision(2) << res << '\n';
}

D.nIM gAME

很Guess的一道题,先手必输,手玩一下发现,当进行到最后是,先手将赢的时候,后手总有办法能破坏先手的策略。

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int mod = 1e9 + 7;

void solve()
{
    ll n;
    cin >> n;
    cout << "lose" << '\n';
}
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _ = 1;
    cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

E.Checksum

赛时都过的很慢,其实很简单(
首先很容易发现,A序列的影响只与A中1的个数有关。
然后k的数据范围又很小,可以考虑枚举k,也就是B中1的个数。
这样的话总共1的个数就确定了,由于只保留k位,再此时1的总个数 mod (1<<k)
这样的话B就确定下来了,此时直接检查B是否满足要求即可
这个题我被坑的点是B可以有前导零(

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int mod = 1e9 + 7;

void solve()
{
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    int cnt = 0;
    for (auto x : s)
    {
        if (x == '1')
        {
            cnt++;
        }
    }
    for (int i = 0; i <= k; i++)
    {
        int px = cnt + i;
        px %= (1 << (k));
        int ok = 0;
        for (int j = 0; j < k; j++)
        {
            if (px >> j & 1)
            {
                ok++;
            }
        }
        if (ok == i)
        {

            for (int j = k - 1; j >= 0; j--)
            {
                if (px >> j & 1)
                {
                    cout << 1;
                }
                else
                    cout << 0;
            }
            cout << '\n';
            return;
        }
    }
    cout << "None" << '\n';
}

A.Paper Watering

很典的一道题,也十分考验细心程度(爽吃罚时
首先我们可以对一个数一直进行平方操作来产生不同的数(注意1要特判
而且只要最初的底数不同,后续不会产生相同的数字
对于开平方操作,只要被开数不是完全平方数,那么新产生的数也满足上述条件
由于一个数被开根的次数很少(log级别),所以我们直接模拟即可,在过程中统计答案

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int mod = 1e9 + 7;

void solve()
{
    ll x, k;
    cin >> x >> k;
    ll ans = 1;
    if (x != 1)
        ans += k;
    ll res = 0;
    ll ps = k;
    while (1)
    {
        if (ps == 0)
            break;
        if (x == 1)
            break;
        res++;
        ll pre = x;
        x = sqrt(x);
        ps--;
        if (ps == 0)
            break;
        ll y = x * x;
        if (y != pre && x != 1)
        {
            res += ps;
        }
    }
    ans += res;
    cout << ans;
}

L. Bracket Generation

给人很亲切的感觉,却又无能为力(
官方题解感觉说的太抽象了(蒟蒻看不懂
这里写一个较为容易的写法
我们将第一种操作后的序列视为最基本的括号序列,可以发现,
只要满足第二种操作内序列已被构造出来的情况,我们就可以在剩余的操作中任选一次进行该操作
对于多重嵌套的序列也一样。
这样我们可以进行一次括号匹配,来判断每次操作二有几种选择,时间复杂度O(n)

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e3 + 7;
const int mod = 998244353;
const double eps = 1e-9;

void solve()
{
    string s;
    cin >> s;
    stack<int> st;
    vector<int> v;
    for (int i = 0; i < s.size(); i++)
    {
        if (s[i] == '(')
        {
            st.push(i);
        }
        else
        {
            if (st.top() == i - 1)
            {
                v.push_back(1);
            }
            else
                v.push_back(2);
            st.pop();
        }
    }
    reverse(v.begin(), v.end());
    ll ans = 1, cnt = 0;
    for (int i = 0; i < v.size(); i++)
    {
        cnt++;
        if (v[i] == 2)
        {
            ans = ans * cnt % mod;
        }
    }
    cout << ans << '\n';
}

M.House

vp时被此题折磨2小时无果下班(不要碰几何!!!
教训是不要再用解析式的方式(中学思维)写算法题,不然被精度和码量双重折磨,害
这里建议使用官方题解的方式,配合叉积真的很好写(叉积早用早爽
(n^2)复杂度枚举射线,对于位于射线左侧(叉积<0)的判断能否成为房顶
对于另一侧的判断能否构成底部(使用map维护距离,点积判断垂直
最后相乘即可得到贡献。
总复杂度O(n^3 * log n)
而且有个很方便的地方是直接使用int64即可,无需考虑精度问题(win

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e3 + 7;
const int mod = 998244353;
struct Point
{
    ll x, y;
    ll operator^(Point b) const
    {
        return x * b.y - y * b.x;
    }
    ll operator*(Point b) const
    {
        return x * b.x + y * b.y;
    }
} p[N];
int n;
Point operator-(Point x, const Point &y)
{
    x.x -= y.x, x.y -= y.y;
    return x;
}
ll dis(Point a, Point b)
{
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
ll solve(Point A, Point B)
{
    Point V = (B - A);
    ll L = 0, R = 0;
    map<ll, int> mp1, mp2;
    for (int i = 1; i <= n; i++)
    {
        Point M = (p[i] - A);
        if ((V ^ M) == 0)
            continue;
        if ((V ^ M) < 0)
        {
            if (dis(p[i], A) == dis(p[i], B))
            {
                L++;
            }
        }
        else
        {
            Point Q = p[i] - B, P = p[i] - A;
            if (Q * V == 0)
            {
                mp2[dis(p[i], B)]++;
            }
            if (V * P == 0)
            {
                mp1[dis(p[i], A)]++;
            }
        }
    }
    // cout << L << '\n';
    for (auto [x, y] : mp1)
    {
        if (mp2[x])
        {
            R++;
        }
    }
    // cout << L << ' ' << R << '\n';
    return L * R;
}
void solve()
{
    // int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> p[i].x >> p[i].y;
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (i == j)
                continue;
            ans += solve(p[i], p[j]);
        }
    }
    cout << ans << '\n';
}

I.Password

不是哥们,正解真1e8啊(
题目要求需要保证每一个数都存在于一个连续的排列当中,问方案数,考虑dp求解
令f[i]表示以i结尾刚好构成一个新的1-k的排列,最后答案就是f[n]
考虑转移,对于新加入的j个数字,使得构成以i结尾的排列,
令加入j个数字新产生的方案数为g[j],则\(f[i] = \sum^k_{j=1}{f[i-j] * g[j]}\)
考虑如何求解g[i],因为只有添加完i个数字之后才可以构成新排列,所以在添加完\([1,i)\)个数字的过程中
不能构成新排列,我们可以容斥
总的方案数有\(i!\),我们可以枚举断点j,使得添加完第j个不构成新排列,方案数是\(j!\),此时注意,后面的
\([j,i]\)添加后要构成新排列(满足g的定义),所以要再乘上\(g[i-j]\)
\(g[i] = i! - \sum_{j=1}^{i-1}{j! * g[i-j]}\)
注意初始化\(f[k] = k!\)

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int mod = 998244353;
const int N = 1e5 + 7;
ll jc[N];
ll f[N], g[N];
void init()
{
    jc[0] = 1;
    for (int i = 1; i <= 100000; i++)
    {
        jc[i] = jc[i - 1] * i % mod;
    }
}
void solve()
{
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= k; i++)
    {
        g[i] = jc[i];
        for (int j = 1; j < i; j++)
        {
            g[i] = g[i] + ((-(jc[j] * g[i - j] % mod)) % mod + mod) % mod;
            g[i] %= mod;
        }
    }
    f[k] = jc[k];
    for (int i = k + 1; i <= n; i++)
    {
        for (int j = 1; j <= k; j++)
        {
            if (j > i)
                break;
            f[i] = f[i] + f[i - j] * g[j] % mod;
            f[i] %= mod;
        }
    }
    cout << (f[n] + mod) % mod << '\n';
}

H.Meet

使得最大值最小,二分答案,考虑如何check
这里采用官方题解的思路,感觉比较巧妙
设你二分的距离为k
枚举m个点对,如果两个点的距离小于等于k,则任意点都可以
如果距离大于k,我们不妨令1为根
对于一个点对(x,y),我们先使得x满足要求
如果x到LCA(x,y)的距离大于k,则以x的k级祖先的任意儿子为根,x所产生的贡献满足k
否则的话,设x的合法贡献为p,x与y的距离为dis,y的贡献为q,则需满足\(p+q=dis\)
因为\(p<=k\),所以\(q>=dis-k\),则y的dis - k - 1级祖先的子树上所有节点都不能作为根
y也同理
用dfs序处理上述问题则会得到若干区间,使用差分维护,最后检查是否存在交集即可成功check

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e5 + 7;
vector<pii> p;
struct HLD
{
    int n;
    std::vector<int> siz, top, dep, parent, in, out, seq;
    std::vector<std::vector<int>> adj;
    int cur;

    HLD() {}
    HLD(int n)
    {
        init(n);
    }
    void init(int n)
    {
        this->n = n;
        siz.resize(n);
        top.resize(n);
        dep.resize(n);
        parent.resize(n);
        in.resize(n);
        out.resize(n);
        seq.resize(n);
        cur = 0;
        adj.assign(n, {});
    }
    void addEdge(int u, int v)
    {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void work(int root = 0)
    {
        top[root] = root;
        dep[root] = 0;
        parent[root] = -1;
        dfs1(root);
        dfs2(root);
    }
    void dfs1(int u)
    {
        if (parent[u] != -1)
        {
            adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u]));
        }

        siz[u] = 1;
        for (auto &v : adj[u])
        {
            parent[v] = u;
            dep[v] = dep[u] + 1;
            dfs1(v);
            siz[u] += siz[v];
            if (siz[v] > siz[adj[u][0]])
            {
                std::swap(v, adj[u][0]);
            }
        }
    }
    void dfs2(int u)
    {
        in[u] = cur++;
        seq[in[u]] = u;
        for (auto v : adj[u])
        {
            top[v] = v == adj[u][0] ? top[u] : v;
            dfs2(v);
        }
        out[u] = cur;
    }
    int lca(int u, int v)
    {
        while (top[u] != top[v])
        {
            if (dep[top[u]] > dep[top[v]])
            {
                u = parent[top[u]];
            }
            else
            {
                v = parent[top[v]];
            }
        }
        return dep[u] < dep[v] ? u : v;
    }

    int dist(int u, int v)
    {
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }

    int jump(int u, int k)
    {
        if (dep[u] < k)
        {
            return -1;
        }

        int d = dep[u] - k;

        while (dep[top[u]] > d)
        {
            u = parent[top[u]];
        }

        return seq[in[u] - dep[u] + d];
    }
};

void solve()
{
    int n, m;
    cin >> n >> m;
    HLD s(n);
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        u--, v--;
        s.addEdge(u, v);
    }
    for (int i = 0; i < m; i++)
    {
        int x, y;
        cin >> x >> y;
        x--, y--;
        p.push_back({x, y});
    }
    s.work();
    auto check = [&](int k)
    {
        vector<int> c(n + 1, 0);
        for (auto [x, y] : p)
        {
            int dis = s.dist(x, y);
            if (dis <= k)
            {
                c[0] += 2;
                continue;
            }
            int fa = s.lca(x, y);
            for (int j = 0; j <= 1; j++)
            {
                int disfa = s.dist(fa, x);
                if (disfa > k)
                {
                    int kfa = s.jump(x, k);
                    c[s.in[kfa]]++;
                    c[s.out[kfa]]--;
                }
                else
                {
                    int kfa = s.jump(y, dis - k - 1);
                    if (kfa == -1)
                        return false;
                    c[0] += 1;
                    c[s.in[kfa]]--;
                    c[s.out[kfa]]++;
                }
                swap(x, y);
            }
        }
        if (c[0] >= 2 * m)
            return true;
        for (int i = 1; i < n; i++)
        {
            c[i] += c[i - 1];
            if (c[i] >= 2 * m)
            {
                return true;
            }
        }
        return false;
    };
    int l = 0, r = n;
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << l << '\n';
}

F.Factor

本来毫无头绪,一看官方题解说直接爆搜,才1e8个答案,一下子简单起来
在k进制下使得能完整表示,那么可以直接理解为\(p*k^{+\infty} = aq(a为正整数)\)
所以对于q进行质因数分解后,里面的质因子要么是p的质因子(有最高次限制),要么是k的因子,无限制
所以我们分解完p和k后直接爆搜即可

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e5 + 7;
ll p, x, k;
vector<ll> pp, pk, cntp;
ll ans = 0;
void dfs(int ok, ll now, int id)
{
    if (ok == 0 && id == pp.size())
    {
        dfs(1, now, 0);
        return;
    }
    // cerr << 1 << '\n';
    if (ok && id == pk.size())
    {
        ans++;
        return;
    }
    if (ok == 0)
    {
        for (int i = 0; i <= cntp[id]; i++)
        {
            dfs(ok, now, id + 1);
            if (now > x / pp[id])
            {
                return;
            }
            now *= pp[id];
        }
    }
    else
    {
        for (int i = 0;; i++)
        {
            dfs(ok, now, id + 1);
            if (now > x / pk[id])
            {
                return;
            }
            now *= pk[id];
        }
    }
}
void solve()
{
    cin >> p >> x >> k;
    map<ll, int> mp; // 标记k的因子;
    for (int i = 2; i <= k / i; i++)
    {
        if (k % i == 0)
        {
            while (k % i == 0)
            {
                k /= i;
            }
            pk.push_back(i);
            mp[i] = 1;
        }
    }
    if (k > 1)
        pk.push_back(k);
    for (int i = 2; i <= p / i; i++)
    {
        if (p % i == 0)
        {
            int cnt = 0;
            while (p % i == 0)
            {
                cnt++;
                p /= i;
            }
            if (mp.count(i))
                continue;
            pp.push_back(i);
            cntp.push_back(cnt);
        }
    }
    if (p > 1 && !mp[p])
    {
        pp.push_back(p);
        cntp.push_back(1);
    }
    dfs(0, 1, 0);
    cout << ans << '\n';
}
posted @ 2024-05-22 19:38  qzhfx  阅读(1589)  评论(7编辑  收藏  举报