[AH2017/HNOI2017]礼物 解题报告

[AH2017/HNOI2017]礼物 解题报告

标签: NTT


题意

有两个手链, 这两个手链上分别有 \(n\) 个装饰品, ($ 1 \le n \le 5 \times 10^4$).
每个装饰品都有一个亮度, 两个手链上装饰品的亮度分别为 \(a_i,\ b_i\), 且 \(1 \le a_i,\ b_i \le m\), (\(1 \le m \le 100\)).

现可以将一个手链上的所有装饰品的亮度值增加一个非负正数 \(c\), 并可以将手链进行旋转, 使得
\[ \sum_{i=1}^{n} (a_i-b_i)^2 \]
最小, 并求出这个最小值

思路

为了描述方便, 我们把 $ \sum_{i=1}^{n} (a_i-b_i)^2 $ 这个式子称作 "亮度差".

现考虑把一个手链的亮度值增加 \(c\) 后, 亮度差会如何变化,
前后亮度差的差值为
\[ \begin{aligned} \sum_{i=1}^{n} [(a_i+c)-b_i]^2 - \sum_{i=1}^{n} (a_i-b_i)^2 &= \sum_{i=1}^{n} c^2 + \sum_{i=1}^{n} 2a_ic - \sum_{i=1}^{n} 2b_ic \\ &= nc^2 + 2c\sum_{i=1}^{n} a_i - b_i \\ \end{aligned} \]

惊奇地发现, 无论装饰品的对应关系如何, 即不管手链如何旋转, 亮度差的变化值是一定的, 并且是关于亮度增加值 \(c\) 的一个二次函数, 可以直接求得最小值.

这样的话, 我们就只需要考虑如何旋转手链能使得初始亮度差最小.

初始亮度差为,
\[ \sum_{i=1}^{n} (a_i - b_i)^2 = \sum_{i=1}^{n} a_i^2 + b_i^2 - 2\sum_{i=1}^{n} a_ib_i \]

要是上述式子最小, 就需要求到 \(\sum_{i=1}^{n} a_ib_i\) 的最大值.

枚举两个对应装饰品之间的距离 \(k\), 则 \(h[k]=\sum_{i=1}^{n} a_ib_{(i+k) \% m}\). ( 设 \(b_0 = b_n\) )
为了化成加法卷积的形式, 设 \(a_{-i}=a_i\)\(h[k] = \sum_{i=1}^{n} a_{-i}b_{(i+k) \%m}\),
然后就可以愉快地 \(NTT\) 了, 还要记得把 \(a_i\) 往前推 \(n\) 位, 并倍长 \(b\)\(a\) ( 说实话我也不太清楚为什么这样可以, 但它就是可以... ).

代码

#include
#define ll long long
#define db double
using namespace std;
const int _=3e5+7;
const int p=998244353;
const int rt=3;
bool be;
int n,m,a[_],b[_],f[_],g[_],t,invt,invrt,num[_];
bool en;
int q_pow(int a,int k){
  int res=1;
  while(k){
    if(k&1) res=(ll)res*a%p;
    a=(ll)a*a%p; k>>=1;
  }
  return res;
}
void NTT(int *f,int id){
  for(int i=0;i>1;
    int w1=q_pow(id==1 ?rt :invrt,(p-1)/len);
    for(int k=0;k>n>>m;
  for(int i=1;i<=n;i++){ scanf("%d",&a[i]); a[0]+=a[i]; } 
  for(int i=1;i<=n;i++){ scanf("%d",&b[i]); b[0]+=b[i]; }
  int A=-n,B=2*(a[0]-b[0]);
  db tmp=(db)-B/(2*A);
  db t1=tmp-floor(tmp),t2=ceil(tmp)-tmp;
  int c= t1>1]>>1)|((i&1) ?t>>1 :0);
  NTT(f,1);
  NTT(g,1);
  for(int i=0;i

你可能感兴趣的:([AH2017/HNOI2017]礼物 解题报告)