[AH2017/HNOI2017]礼物

传送门

这道题我一开始做的时候啥也不会……心想这和FFT有啥关系啊……

旁边的\(mrclr\)告诉我说要勇于把式子分解。然后我就把式子分解了一下。我们要求的是\(\sum_{i=0}^n(x_i - y_i - c)^2\),拆开之后就是\(\sum_{i=0}^n(x_i^2 + y_i^2 - 2x_iy_i - 2(x_i-y_i)c + c^2)\)

由于m的范围很小,增加(减少)的亮度可以直接枚举,然后这好像式子中只有\(\sum_{i=0}^nx_iy_i\)不是已知的量?

这个式子的形式非常的熟悉,我们只要把其中一个的顺序倒过来,那么就变成了\(\sum_{i=0}^nx_{n-i}y_i\),就成为了卷积的形式,可以用FFT维护。

但是现在旋转是不确定的,总不能每旋转一次都用FFT计算一次吧……

这时候旁边的\(mrclr\)又告诉我,其实一次FFT就可以完事。想到卷积的形式,他其实只是两段长为n的区间互相乘积的和
……那我们就像做环的题一样,先把其中一段序列倒过来并且复制一段在后面,直接拿这段长的和另一段序列卷积,最后只取合适范围内的结果就行了。

然后我写了以后数一直不对……发现自己在最后枚举c的时候忘记乘以n了……

看一下代码。PS:这题数据好像很水……我调试的时候只在正负10内枚举的竟然能过……

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<vector>
#include<map>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define fr friend inline
#define y1 poj
#define mp make_pair
#define pr pair<int,int>
#define fi first
#define sc second
#define pb push_back

using namespace std;
typedef long long ll;
const int M = 200005;
const ll INF = 1e15;
const double eps = 1e-7;
const double pi = acos(-1);
const ll mod = 998244353;

ll read()
{
    ll ans = 0,op = 1;char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
    return ans * op;
}

struct Comp
{
   double x,y;
   Comp(){}
   Comp(double px,double py){x = px,y = py;}
   fr Comp operator + (const Comp &a,const Comp &b) {return (Comp){a.x + b.x,a.y + b.y};}
   fr Comp operator - (const Comp &a,const Comp &b) {return (Comp){a.x - b.x,a.y - b.y};}
   fr Comp operator * (const Comp &a,const Comp &b) {return (Comp){a.x * b.x - a.y * b.y,a.y * b.x + a.x * b.y};}
}a[M<<1],b[M<<1],kx,ky;

int n,m,len = 1,L,rev[M<<1];
ll maxn,c[M<<1],suma,sumb,ans = INF;

void fft(Comp *a,int f)
{
   rep(i,0,len-1) if(i < rev[i]) swap(a[i],a[rev[i]]);
   for(int i = 1;i < len;i <<= 1)
   {
      Comp omg1(cos(pi / i),f * sin(pi / i));
      for(int j = 0;j < len;j += (i << 1))
      {
     Comp omg(1,0);
     rep(k,0,i-1)
     {
        kx = a[k+j],ky = omg * a[k+j+i];
        a[k+j] = kx + ky,a[k+j+i] = kx - ky,omg = omg * omg1;
     }
      }
   }
}

int main()
{
   n = read() - 1,m = read();
   rep(i,0,n) a[n-i].x = a[(n<<1)+1-i].x = read(),suma += (a[n-i].x * a[n-i].x),sumb += a[n-i].x;
   rep(i,0,n) b[i].x = read(),suma += (b[i].x * b[i].x),sumb -= b[i].x;
   //rep(i,0,(n<<1)+1) printf("%.2lf ",a[i].x);enter;
   //rep(i,0,n) printf("%.2lf ",b[i].x);enter;
   while(len <= n * 3) len <<= 1,L++;
   rep(i,0,len-1) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (L-1));
   fft(a,1),fft(b,1);
   rep(i,0,len-1) b[i] = b[i] * a[i];
   fft(b,-1);
   rep(i,0,len-1) c[i] = (ll)floor(b[i].x / len + 0.5);
   //rep(i,0,len-1) printf("#%lld ",c[i]);enter;
   //printf("$%lld %lld\n",suma,sumb);
   rep(i,n,2*n-1) maxn = max(maxn,c[i]);
   //printf("%lld\n",maxn);
   rep(i,-100,100) ans = min(ans,suma - 2 * sumb * i + i * i * (n+1) - 2 * maxn);
   printf("%lld\n",ans);
   return 0;
}

posted @ 2018-12-12 21:34  CaptainLi  阅读(156)  评论(0编辑  收藏  举报