Loading

11.26 CW 模拟赛 赛时记录

看题

也是给他绑上 \(\rm{Subtask}\) 了, 这我骗鸡毛分啊

感冒也是非常难受, 但是事已至此, 先读题吧


题目背景好看爱看

\(\rm{T1}\)

图论题, 好玩

\(\rm{T2}\)

大概也是不会做, 再说

\(\rm{T3}\)

难绷, 考虑高档暴力

\(\rm{T4}\)

这个肯定是暴力都难打


今天也是 \(30 \rm{min} + 1 \rm{h} + 15 \rm{min} + 1 \rm{h} 15 \rm{min}\)

\(\rm{T1}\)

先分析题意,

给定一张无重边, 无环的无向图
求加若干边之后, 图中最长链能达到多长

首先发现最后的图一定包含了全部的 \(n\) 个点, 因为如果不包含点 \(i\) , 那么选上点 \(i\) 之后, 最长的链一定会变得更长

容易的, 我们发现原图应该是一个森林, 那么我们需要把原图中的森林连接起来, 使得最长链最长, 符合直觉的, 我们应该去连接每颗树的直径

那么好, 这题结束了


首先读入是显然的, 对于每一个连通块, 两遍 \(\rm{dfs}\) 求直径的两端

对于第 \(i\) 个连通块, 我们考虑将 \(i - 1\) 连通块的右侧 (这里的左右人为指定) , 和 \(i\) 的左侧连接起来, 对于 \(i = 1/n\) 情况特殊处理

\(\rm{T2}\)

只过了 \(25 \rm{min}\) , 冲冲冲

\(\rm{T1}\) 多半是大众分, 当然我多半拿不到大众分就是了 (确实没拿到)


转化题意

对于序列 \(s\) , 找出一段区间 \([L, R]\) , 使得区间长度至少为 \(k\) 的前提下, 令所有数的 \(\gcd\)\(g\) , 求 \(\displaystyle g \times \sum_{i = L}^{R} s_i \to \max\)

题意其实给的很清楚了, 不太需要转化

区间长是 \(10^6\) 级别的, 不好处理

暴力的做法是, 考虑对于一个固定的 \(R\) , 我们向前走, 记录 \(\sum\) 值和 \(\gcd\) , 可以做到 \(\mathcal{O} (n ^ 2)\) 的时间复杂度, \(30 \rm{pts}\)

我们还需要优化, 这里优化的点很明确, 我们需要利用之前的计算结果, 不能再次重复计算

但是并不好利用之前的计算结果, 考虑神秘优化

注意到当右端点确定, \(\gcd\) 的趋势是单调不增, 我们考虑预处理出所有 \(\gcd\) 的变化区间的最左侧点, 只考虑最左端点即可, 期望上是 \(\mathcal{O} (n \sqrt{n})\) 的, 可以拿到 \(60 \rm{pts}\)


一会再回来想一想

对于右端点的每一次拓张, 我们向左枚举到第一个值为当前 \(s_i\) 因数的最左侧点, 中间的全部都要并过来, 即在栈中弹出, 如果左边没有因数, 弹出完了之后插入一个 \(1\) , 特别的, 判断一下 \(s_i\) 和当前栈头的关系, 看 \(s_i\) 是否需要单开区间

这个可以拿栈处理

\(\rm{T3}\)

转化题意

对于序列 \(s\) , 找出多少个区间 \([L, R]\) 满足其中有一半以上的元素相同

对于 \(\rm{Subtask} 1, 2\) , \(\mathcal{O} (n ^ 2 \log n)\) 算法可以解决

对于 \(\rm{Subtask} 3\) ,

我们先预处理出两种数在区间中的出现次数的差出现次数的前缀和, 然后就简单了

代码

没有好实现的, 按分值打

\(\rm{T1}\)

写的差不多, 看看会不会挂

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

#define FILE_IO

int n, m;

class Graph_Class
{
private:

public:
    /*并查集*/
    struct DSU_struct
    {
        int fa[MAXN];
        void fa_init() { for (int i = 1; i <= n; i++) fa[i] = i; }

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

        void merge(int u, int v) {
            int fa_u = find(u), fa_v = find(v);
            fa[fa_v] = fa_u;
        }
    } DSU;

    struct node
    {
        int to;
        int next;
    } Edge[MAXN << 1];
    int Edge_cnt = 0;
    int head[MAXN];
    void Edge_init() { for (int i = 1; i <= m * 2; i++) Edge[i].next = -1; }
    void head_init() { for (int i = 1; i <= n; i++) head[i] = -1; }
    void addedge(int u, int v) {
        Edge[++Edge_cnt].to = v;
        Edge[Edge_cnt].next = head[u];
        head[u] = Edge_cnt;
    }
} Graph;

class Sol_Class
{
private:
    int dist[MAXN];
    void dfs1(int Now, int fa, int d) {
        dist[Now] = d;
        for (int i = Graph.head[Now]; ~i; i = Graph.Edge[i].next) {
            int NowTo = Graph.Edge[i].to;
            if (NowTo == fa) continue;

            dfs1(NowTo, Now, d + 1);
        }
    }

    int CalcD(int NowTree) {
        for (int i = 1; i <= n; i++) {
            if (Graph.DSU.find(i) == NowTree) {
                dfs1(i, -1, 1);
                break;
            }
        }

        int Root = -1, maxdis = 0;
        for (int i = 1; i <= n; i++) {
            if (Graph.DSU.find(i) != NowTree) continue;
            if (dist[i] > maxdis) maxdis = dist[i], Root = i;
        }

        memset(dist, 0, sizeof(dist));
        dfs1(Root, -1, 1);

        Root = -1, maxdis = 0;
        for (int i = 1; i <= n; i++)
        {
            if (Graph.DSU.find(i) != NowTree) continue;
            if (dist[i] > maxdis) maxdis = dist[i], Root = i;
        }

        return maxdis;
    }

public:
    bool Treevis[MAXN];
    int Ans = 0;
    /*分割森林, 计算直径*/
    void solve()
    {
        for (int i = 1; i <= n; i++) {
            int NowTree = Graph.DSU.find(i);
            if (Treevis[NowTree]) continue;

            Ans += CalcD(NowTree);
            Treevis[NowTree] = true;
        }

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

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

    scanf("%lld %lld", &n, &m);
    Graph.DSU.fa_init();
    Graph.head_init();
    Graph.Edge_init();
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%lld %lld", &u, &v);
        Graph.addedge(u, v), Graph.addedge(v, u);
        Graph.DSU.merge(u, v);
    }

    Sol.solve();

    return 0;
}

\(\rm{T2}\)

稍微复杂, 看运气了, 话说我今天 \(\rm{luogu}\) 都没打卡

优化方法可能是错的, 跑不过大样例, 只能留在后面看看能骗多少

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

#define FILE_IO

int n, k;
int h[MAXN];
int Sum[MAXN];

class Subtask_3
{
private:
    /*栈*/
    struct node {
        int Pos;
        int Val;
    } ;
    struct Stack_struct
    { 
        node Stack[MAXN];
        int tp = 0;

        bool empty() {
            return tp == 0;
        }

        void pop() {
            tp--;
        }

        void push(node x) {
            Stack[++tp] = x;
        }

        node top() {
            return Stack[tp];
        }
    } S;

public:

/*处理*/
    void solve()
    {
        int Ans = 0;
        for (int i = 1; i <= n; i++)
        {
            /*处理 gcd 左端点*/
            while (!S.empty()) {
                node Now = S.top();
                if (h[i] % Now.Val == 0) break;
                S.pop();
            }

            if (S.empty()) S.push({1, 1});
            if (h[i] != S.top().Val) S.push({i, h[i]});

            /*计算答案*/
            for (int j = 1; j <= S.tp; j++) {
                int NowAns = (Sum[i] - Sum[S.Stack[j].Pos - 1]) * S.Stack[j].Val;
                if (i - S.Stack[j].Pos + 1 < k) continue;
                Ans = std::max(Ans, NowAns);
            }
        }

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

class Subtask_12
{
private:

public:
    void solve()
    {
        int Ans = 0;
        for (int i = 1; i <= n; i++) {
            int Sum = 0, Gcd  = h[i];
            for (int j = i; j >= 1; j--) {
                Sum += h[j];
                Gcd = std::__gcd(Gcd, h[j]);

                if (i - j + 1 >= k) Ans = std::max(Ans, Sum * Gcd);
            }
        }

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

signed main()
{
#ifdef FILE_IO
    freopen("intelligence.in", "r", stdin);
    freopen("intelligence.out", "w", stdout);
#endif
    scanf("%lld %lld", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &h[i]), Sum[i] = Sum[i - 1] + h[i];
    
    if (n > 10000) S_3.solve();
    else S_12.solve();

    return 0;
}

只能赶紧冲 \(\rm{T3}\) , 优化思路能骗 \(10 \rm{pts}\) 就非常好了

\(\rm{T3}\)

没打完, 寄

posted @ 2024-11-26 14:53  Yorg  阅读(12)  评论(0)    收藏  举报