HNOI 2017 礼物

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一 个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。但是在她生日的前一天,我的室友突 然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有 装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它, 但是由于上面 装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差 异值最小。在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 1,2,…,n, 其中 n 为每个手环的装饰物个数,第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手 环的 i 号位置装饰物 亮度为 yi,两个手环之间的差异值为(参见输入输出样例和样例解释): $\sum\limits_{i=1}^{n}(x_i-y_i)^2$ 麻烦你帮他 计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小, 这个最小值是多少呢?

$n\leq 50000,m \leq 100$

sol:

首先根据 GXZlegend 大神的数学推理得到 $c = \frac{\sum x - \sum y}{n}$

大概是有两个环,要求最大的 $\sum\limits_{i=1}^n x_i \times y_i$

直接把 $y$ 翻转过来做个卷积,然后看 $n\sim 2n$ 项哪个最小就行了

HNOI 2017 礼物
 
#include<bits/stdc++.h>
#define LL long long
#define DB long double
#define rep(i, s, t) for(register LL i = (s), i##end = (t); i <= i ## end; ++i)
#define dwn(i, s, t) for(register LL i = (s), i##end = (t); i >= i ## end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar())if(ch == '-') f=-f;
    for(;isdigit(ch);ch=getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 500010;
const DB pi = acos(-1);
struct cp {
    DB x, y;
    cp(){}
    cp(DB _1, DB _2) : x(_1), y(_2){}
    cp operator + (const cp &b) const { return cp(x + b.x, y + b.y); }
    cp operator - (const cp &b) const { return cp(x - b.x, y - b.y); }
    cp operator * (const cp &b) const { return cp(x * b.x - y * b.y, x * b.y + y * b.x); }
}cu[maxn], cv[maxn];
int r[maxn], lg[maxn + maxn], a[maxn], b[maxn];
void fft(cp *a, int n, int f) {
    rep(i, 1, n-1) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (lg[n] - 1));
    rep(i, 1, n-1) if(i < r[i]) swap(a[i], a[r[i]]);
    for(int i=1;i<n;i<<=1) {
        cp wn = cp(cos(pi / i), f * sin(pi / i));
        for(int j=0;j<n;j+=(i<<1)) {
            cp w = cp(1, 0);
            for(int k=0;k<i;k++,w=w*wn) {
                cp x = a[j + k], y = w * a[j + k + i];
                a[j + k] = x + y;
                a[j + k + i] = x - y;
            }
        }
    }
    //if(f == -1) rep(i, 0, n-1) a[i].x = round(a[i].x / (DB)n);
}
int n, m;
int main() {
    DB sumx = 0.0, sumy = 0.0;
    lg[0] = -1; rep(i, 1, 500000) lg[i] = lg[i >> 1] + 1;
    n = read(), m = read(); DB c = 0.0;
    rep(i, 0, n-1) a[i] = read(), c -= a[i];
    rep(i, 0, n-1) b[i] = read(), c += b[i]; c = round(c / n);
    rep(i, 0, n-1) sumx += 1.0 * (a[i] + c) * (a[i] + c), sumy += 1.0 * b[i] * b[i];
    rep(i, 0, n + n - 1) cu[i].x = a[i % n] + c;
    rep(i, 0, n - 1) cv[i].x = b[n - i - 1];
    int len = 1; for(;len <= n+n; len <<= 1);
    fft(cu, len, 1); fft(cv, len, 1);
    rep(i, 0, len) cu[i] = cu[i] * cv[i];
    fft(cu, len, -1);
    DB ans = 1e15;
    rep(i, n-1, n + n - 1) ans = min(ans, sumx + sumy - 2 * round(cu[i].x / (DB)len));
    double res = ans;
    printf("%.0f\n", res);
}
View Code

因为本题值域很小,NTT 也能过

HNOI 2017 礼物
#include<bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for(register LL i = (s), i##end = (t); i <= i ## end; ++i)
#define dwn(i, s, t) for(register LL i = (s), i##end = (t); i >= i ## end; --i)
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar())if(ch == '-') f=-f;
    for(;isdigit(ch);ch=getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 500010, mod = 998244353;
inline int ksm(int x, int t) {
    int res = 1;
    while(t) {
        if(t & 1) res = 1LL * res * x % mod;
        x = 1LL * x * x % mod;
        t = t >> 1;
    } return res;
}
int cu[maxn], cv[maxn];
int r[maxn], lg[maxn + maxn], a[maxn], b[maxn];
void fft(int *a, int n, int f) {
    rep(i, 1, n-1) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (lg[n] - 1));
    rep(i, 1, n-1) if(i < r[i]) swap(a[i], a[r[i]]);
    for(int i=1;i<n;i<<=1) {
        int wn = ksm(3, (mod-1) / (i << 1));
        if(f == -1) wn = ksm(wn, mod - 2);
        for(int j=0;j<n;j+=(i<<1)) {
            int w = 1;
            for(int k=0;k<i;k++,w=1LL*w*wn%mod) {
                int x = a[j + k], y = 1LL * w * a[j + k + i] % mod;
                a[j + k] = (x + y) % mod;
                a[j + k + i] = (x - y + mod) % mod;
            }
        }
    }
    if(f == -1) {
        int inv_n = ksm(n, mod - 2);
        rep(i, 0, n-1) a[i] = 1LL * a[i] * inv_n % mod;
    }
}
int n, m;
int main() {
    int sumx = 0, sumy = 0;
    lg[0] = -1; rep(i, 1, 500000) lg[i] = lg[i >> 1] + 1;
    n = read(), m = read(); int c = 0;
    rep(i, 0, n-1) a[i] = read(), c -= a[i];
    rep(i, 0, n-1) b[i] = read(), c += b[i]; c = round(1.0 * c / n);
    //cout << c << endl;
    rep(i, 0, n-1) sumx += (a[i] + c) * (a[i] + c), sumy += b[i] * b[i];
    rep(i, 0, n + n - 1) cu[i] = a[i % n] + c;
    rep(i, 0, n - 1) cv[i]= b[n - i - 1];
    int len = 1; for(;len <= n+n; len <<= 1);
    fft(cu, len, 1); fft(cv, len, 1);
    rep(i, 0, len) cu[i] = 1LL * cu[i] * cv[i] % mod;
    fft(cu, len, -1);
    int ans = 1e9;
    rep(i, n-1, n + n - 1) ans = min(ans, sumx + sumy - 2 * cu[i]);
    cout << ans << endl;
}
View Code

 

上一篇:[HNOI/AHOI2018]毒瘤


下一篇:「HNOI 2019」白兔之舞