P8632 [蓝桥杯 2015 国 B] 居民集会 题解

其实我也没写什么正经做法……不过能过题

对集会位置退火,考虑对任意集会位置 $a,b,c$ 怎么 $O(1)$ 计算答案。

以 $[1,a]$ 的家庭为例,其贡献为 $\sum\limits_{i=1}^at_i(d_a-d_i)=d_a\sum\limits_{i=1}^a t_i-\sum\limits_{i=1}^ad_it_i$。

分别预处理 $\sum\limits_{i=1}^a t_i$ 和 $\sum\limits_{i=1}^ad_it_i$ 即可。$(a,b],(b,c],(c,n+1]$ 的家庭同理。注意 $d_{n+1}=L$。

初始解贪心地选择人数最多的三个位置。

设当前温度为 $T$,则对每个集会位置 $x\gets (x+pT+n)\bmod n+1$ 生成新解,其中 $p$ 为 $[-1,1]$ 的随机实数。

调一个合适的参数,然后微调初温,发现初温为 $n+1$ 和 $n+2$ 时可以得到两个不同的 90 分。

所以分别跑 600 遍取 $\min$ 即可。

#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int n, L, x, y, z, X, Y, Z, d[100050], t[100050];
long long q, Q, s[100050], p[100050];
bool u;
inline long long F(int a, int b, int c)
{
    int x = a, y = b, z = c;
    if (x > y)
        swap(x, y);
    if (x > z)
        swap(x, z);
    if (y > z)
        swap(y, z);
    return d[x] * s[x] - p[x] + d[y] * (s[y] - s[x]) - p[y] + p[x] + d[z] * (s[z] - s[y]) - p[z] + p[y] + L * (s[n] - s[z]) - p[n] + p[z];
}
inline void SA()
{
    double T = n + u + 1;
    while (T > 1e-7)
    {
        int a = int(x + (rand() * 2.0 / RAND_MAX - 1) * T + n) % n + 1,
            b = int(y + (rand() * 2.0 / RAND_MAX - 1) * T + n) % n + 1,
            c = int(z + (rand() * 2.0 / RAND_MAX - 1) * T + n) % n + 1;
        long long f = F(a, b, c);
        double d = f - q;
        if (d < 0 || exp(-d / T) * RAND_MAX > rand())
            q = f, x = a, y = b, z = c;
        Q = min(Q, q);
        T *= 0.9965;
    }
}
int main()
{
    srand(388651);
    scanf("%d%d", &n, &L);
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d%d", d + i, t + i);
        s[i] = s[i - 1] + t[i];
        p[i] = p[i - 1] + 1ll * d[i] * t[i];
        if (t[i] > t[x])
            z = y, y = x, x = i;
        else if (t[i] > t[y])
            z = y, y = i;
        else if (t[i] > t[z])
            z = i;
    }
    q = Q = F(X = x, Y = y, Z = z);
    for (int i = 0; i < 600; ++i)
        SA();
    u = 1;
    q = F(x = X, y = Y, z = Z);
    srand(388651);
    for (int i = 0; i < 600; ++i)
        SA();
    printf("%lld", Q);
    return 0;
}
posted @ 2023-06-14 09:37  5k_sync_closer  阅读(2)  评论(0编辑  收藏  举报  来源