Loading

11.22 CW 模拟赛 赛时记录

看题

也是把昨天的题调完 + 开大, 开局自带 \(15 \rm{min}\) 劣势(

还是老 \(\rm{Theme}\) 用的爽

机房有人能把薄膜键盘敲出机械的声音

感觉每场都只打了弱智分, 怎么办?

\(\rm{A}\)

我咧个逆序对啊

一眼找规律 + 思维, 应该能做

\(\rm{B}\)

像是图论一类的东西, 观察一下看能不能做

不是哥们, 不给部分分?

\(\rm{C}\)

我不到啊

\(\rm{D}\)

我不到啊


话说每次都看完题正序做, 好像也没什么意义

\(\rm{A}\)

容易的, 我们可以知道数列 \(a\) 的逆序对个数, 因为 \(n \leq 5000\) , 我们直接用 \(\mathcal{O} (n ^ 2)\) 算法即可

注意到 \(k \leq 10 ^ 6\) , 我们是有能力枚举每一个复制串的 (以下记为 \(p\) )

那么问题就简单了

考虑计算每个数在 \(a\) 数列中, 有多少个数比他大(以下设为 \(B_i\))

那么对于第 \(i\) 次重复, 前面就会有 \((i - 1) \times (\sum_{j = 1}^{n} B_j)\) 个自带逆序对, 然后对于这一次复制出来的数列 \(a\) , 再计算带来的逆序对即可

具体的, 枚举每一个复制串, 每一个复制串对答案的贡献为 \((i - 1) \times (\sum_{j = 1}^{n} B_j) + p\) , 总时间复杂度 \(\mathcal{O} (n ^ 2 + k)\)

\(\rm{B}\)

也是传统不写代码, 开就完了

显然的, 我们可以先转化问题
对于无向图上的 \(n\) 个点, 点之间的边权就是 \(\min(\text{图上的欧氏距离的平方和}, v)\) , 求走完所有点时经过的最小边权和

手玩样例看下有没有思路?

显然的, 对于 \(50 \rm{pts}\) , 状压可以解决

想到 \(1 \rm{h}\) 先跳过, 分数已经够多了

考虑剩下的 \(50 \rm{pts}\) , 注意到我们的目标是在完全图中掏一个长为 \(n\) 的环出来使得边权和最小, 显然可以状态压缩 , 由于是完全图, 状态膨胀非常快, 大概是 \(n^n\) 这一级的, 这一点也不好处理, 兴许有些妙妙做法可以做, 但我今天这状态应该止步于此了, \(\rm{lhs}\) 多半又 \(\rm{AK}\)

其实对于后面的点, 我有一个乱搞做法, 枚举起始点, 每个点贪心的选择最近点即可, 但是正确性比较的不显然, 冬天到了也是没有什么打的欲望

考虑剪掉不需要的枝, 然而剪不掉w

顺着状压 \(\rm{dp}\) 往下走
注意到每一条边 \((u, v)\) 都可以更新 \(State_{without \ v \ and \ with \ u} \to State_{with \ v \ and \ with \ u}\)
考虑逆向处理
我们可以知道, 对于每一个点 \(v\) , 我们可以枚举其出边权值最小, 加在一起即可
但是这里有约束条件, 注意到如果 \(u\) 已经不在集合中, 我们不能使用 \((u, v)\)

所以我们要找出 \(n - 1\) 条边, 其中每个点作为终点只出现一次

然后就可以用最小生成树处理了

\(\rm{C}\)

也是大劣势, 出场多半发现其他人都有 \(300 \rm{pts}\) 起手, 好嘛

神秘题目, 也是不太理解, 像 \(\rm{dp}\) 倒是

考虑 \(n \leq 13\)\(40 \rm{pts}\) , 可以 \(\rm{dfs}\) 去处理, 对于每个点, 要么撤回到之前的点, 要么继续往前走, 要么留下来

有点困, 今天又要反向 \(\rm{rk} 1\)

稍微想拿多一点的分就需要想正解, 丢了

\(\rm{D}\)

高档暴力 \(60 \rm{pts}\) 可以打

对于 \(n = 3, 4, 5, 6\) , 直接枚举即可, 判断是简单的

对于 \(x = 1, y = 1, z = 1\) , 直接 \(n ^ 2\) 枚举分段即可
对于 \(x = 1, y = 1, z = 2\) , 考虑 \(n ^ 2\) 枚举的时候加点判断?
最后一段可以是

  • 两个 \(1\)
  • 一个 \(2\)

代码

也是留下接近 \(2\rm{h}\) 来打

\(\rm{A}\)

#include <bits/stdc++.h>
#define int long long
const int MAXN = 5200;

int n, k;
int a[MAXN];

class Sol_Class
{
private:
    int p = 0;

    int App[MAXN];
    int SumB = 0;

    int Ans = 0;

public:
    /*计算数列 a 逆序对个数, 以及 B 的和*/
    void CalcP()
    {
        memset(App, 0, sizeof(App));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                if (a[j] > a[i]) p++;
            }
            App[a[i]]++;
        }

        // std::sort(a + 1, a + n + 1);
        for (int i = 1; i <= n; i++) {
            for (int j = a[i] + 1; j <= 5000; j++)
                SumB += App[j];
        }
    }

    /*计算答案*/
    void solve()
    {
        for (int i = 1; i <= k; i++) {
            Ans += (i - 1) * SumB + p;
        }

        printf("%lld", Ans);
    }
} Sol;

signed main()
{
#ifdef FILE_IO
    freopen("inversion.in", "r", stdin);
    freopen("inversion.out", "w", stdout);
#endif
    scanf("%lld %lld", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }

    Sol.CalcP();
    Sol.solve();

    return 0;
}

虽然很神经, 但是打完了, 用时 \(16 \rm{min}\)

\(\rm{B}\)

建边跑 \(\rm{dp}\)

发现状压往里走应该就是正解, 那就把这个题放到最后去

\(\rm{C}\)

写个 \(\rm{dfs}\) 即可

喵了个咪的打不出来, 丢了

\(\rm{D}\)

#include <bits/stdc++.h>
const int MAXN = 70;

int n, x, y, z;

int s[MAXN];
bool check()
{
    int Sum[MAXN];
    memset(Sum, 0, sizeof(Sum));
    for (int i = 1; i <= n; i++) Sum[i] = Sum[i - 1] + s[i];

    int Cut1 = 0;
    while(Sum[Cut1] < x) Cut1++;
    if (Sum[Cut1] != x) return false;

    int Cut2 = Cut1;
    while(Sum[Cut2] - Sum[Cut1] < y) Cut2++;
    if (Sum[Cut2] - Sum[Cut1] != y) return false;

    if (Sum[n] - Sum[Cut2] != z) return false;
    return true;
}

int Ans = 0;
void dfs(int Now)
{
    if (Now == n) {
        if (check())
            Ans++;
        return;
    }
    for (int i = 1; i <= 10; i++) {
        s[Now + 1] = i;
        dfs(Now + 1);
        s[Now + 1] = 0;
    }
}

int main()
{
#ifdef FILE_IO
    freopen("subsequence.in", "r", stdin);
    freopen("subsequence.out", "w", stdout);
#endif

    scanf("%d %d %d %d", &n, &x, &y, &z);

    if (n <= 6)
    {
        dfs(0);
        printf("%d", Ans);
    } else{
        if(z == 1)
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n && i + j < n; j++) {
                    int k = n - i - j;
                    Ans += i * j * k;
                }
            }
        else
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n && i + j < n; j++) {
                    int k = n - i - j;
                    if (k == 1) Ans += i * j * k;
                    else Ans += i * j * (k * (k - 1) / 2) + i * j * k;
                }
            }

        printf("%d", Ans);
    }

    return 0;
}

欧呦, 谢天谢地了, \(30 \rm{min}\) 还要想 \(\rm{B}\) , 从某种意义上来讲, 我是备战明天比赛的第一个 \(\rm{Man}\)

posted @ 2024-11-22 14:47  Yorg  阅读(18)  评论(0)    收藏  举报