分享

[NOI Online 提高组]第三题-最小环 解析

 长沙7喜 2020-03-17

摘要

    NOI Online 提高组试题解析

结论题:

首先我们将相乘的数连边,能看出来会形成几个环,显然环与环之间互不影响。

然后易证一共有

个环,每个环里就有

个数,显然我们给每个环分配的数连续更优。

关于一个环里怎么分配:我们 看样例/手玩可知 将所有的数从小到大排序依次塞入,左一个右一个最优,其实也很好理解,这样我们能将较大的数尽可能的挨在一起(考虑它是一个环)。

下面是考场代码,可能有点丑.

#include<algorithm>

#include<iostream>

#include<cstdio>

#define LL long long

using namespace std;

int n, m, k, t;

const int N = 200010;

int a[N];

LL ans[N];

inline int read()

{

    int res = 0; char ch = getchar(); bool XX = false;

    for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);

    for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);

    return XX ? -res : res;

}

int GCD(int a, int b) {return b ? GCD(b, a % b) : a;}

void solve1()

{

    while (m--)

    {

        k = read();

        if (ans[k]) {printf('%lld\n', ans[k]); continue;}

        if (k == 0)

        {

            for (int i = 1; i <= n; ++i)ans[k] += (LL)a[i] * a[i];

            printf('%lld\n', ans[k]);

            continue;

        }

        if (2 * k == n)

        {

            for (int i = 1; i <= n; i += 2)ans[k] += (LL)a[i] * a[i + 1];

            ans[k] *= 2;

            printf('%lld\n', ans[k]);

            continue;

        }

        t = n / GCD(n, k); //gcd 个环

        for (int i = 1; i <= n; i += t)

            for (int j = i, to = i + t - 1; j <= to - 1; ++j)

            {

                if (j == i)ans[k] += (LL)a[j] * a[j + 1] + (LL)a[j] * a[j + 2];

                else if (j == to - 1)ans[k] += (LL)a[j] * a[j + 1];

                else ans[k] += (LL)a[j] * a[j + 2];

            }

        printf('%lld\n', ans[k]);

    }

}

void solve2()

{

    while (m--)

    {

        k = read();

        if (k == 0)

        {

            if (!ans[k])for (int i = 1; i <= n; ++i)ans[k] += (LL)a[i] * a[i];

            printf('%lld\n', ans[k]);

            continue;

        }

        k = GCD(n, k);

        if (ans[k]) {printf('%lld\n', ans[k]); continue;}

        if (2 * k == n)

        {

            for (int i = 1; i <= n; i += 2)ans[k] += (LL)a[i] * a[i + 1];

            ans[k] *= 2;

            printf('%lld\n', ans[k]);

            continue;

        }

        t = n / GCD(n, k); //gcd 个环

        for (int i = 1; i <= n; i += t)

            for (int j = i, to = i + t - 1; j <= to - 1; ++j)

            {

                if (j == i)ans[k] += (LL)a[j] * a[j + 1] + (LL)a[j] * a[j + 2];

                else if (j == to - 1)ans[k] += (LL)a[j] * a[j + 1];

                else ans[k] += (LL)a[j] * a[j + 2];

            }

        printf('%lld\n', ans[k]);

    }

}

int main()

{

//  freopen('ring.in','r',stdin);

//  freopen('ring.out','w',stdout);

    cin >> n >> m;

    for (int i = 1; i <= n; ++i)a[i] = read();

    sort(a + 1, a + 1 + n);

    if (n <= 3000)solve1();

    else solve2();

    fclose(stdin); fclose(stdout);

    return 0;

}

本文作者wljss,仅供参考,有任何问题欢迎与我们讨论或者与原作者讨论。原文地址:https://www.cnblogs.com/chdy/p/12451953.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多