zoukankan      html  css  js  c++  java
  • [AH2017/HNOI2017]礼物 解题报告

    [AH2017/HNOI2017]礼物

    标签: NTT


    题意

    有两个手链, 这两个手链上分别有 (n) 个装饰品, ( (1 le n le 5 imes 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) 后, 亮度差会如何变化,
    前后亮度差的差值为

    [egin{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 + 2csum_{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) - 2sum_{i=1}^{n} a_ib_i ]

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

    我们把序列 (a) 翻转, 使得 (a_i = a_{n - i + 1}), 那么上述式子就变为 (sum_{i = 1}^{n} a_{n - i + 1} b_i), 是一个加法卷积的形式.

    我们把 (a) 倍长, 并与 (b) 进行一次卷积, 那么卷积结果 (c) 的第 (n + i) 位就是 (a) 从第 (i) 位开始与 (b) 相乘的结果, 就是题目中的将 (a) 旋转了 (i - 1) 次. 所以我们 NTT 后取 (n + 1 sim 2n) 的最大值即可.

    代码

    #include<bits/stdc++.h>
    #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<t;i++)
        if(i<num[i]) swap(f[i],f[num[i]]);
      for(int len=2;len<=t;len<<=1){
        int gap=len>>1;
        int w1=q_pow(id==1 ?rt :invrt,(p-1)/len);
        for(int k=0;k<t;k+=len){
          int w=1;
          for(int i=k;i<k+gap;i++,w=(ll)w*w1%p){
    	int tmp=(ll)w*f[i+gap]%p;
    	f[i+gap]=(f[i]-tmp+p)%p;
    	f[i]=(f[i]+tmp)%p;
          }
        }
      }
    }
    int main(){
      //freopen("gift.in","r",stdin);
      //freopen("gift.out","w",stdout);
      cin>>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<t2 ?floor(tmp) :ceil(tmp);
      //printf("c: %d
    ",c);
      c=A*c*c+B*c;
      for(int i=0;i<n;i++) f[i]=a[n-i];
      for(int i=1;i<=n;i++) g[i]=b[i];
      for(int i=n+1;i<=2*n;i++) g[i]=b[i-n];
      t=1; while(t<=2*n) t<<=1;
      invt=q_pow(t,p-2); invrt=q_pow(rt,p-2);
      for(int i=0;i<t;i++)
        num[i]=(num[i>>1]>>1)|((i&1) ?t>>1 :0);
      NTT(f,1);
      NTT(g,1);
      for(int i=0;i<t;i++) f[i]=(ll)f[i]*g[i]%p;
      NTT(f,-1);
      ll ans=-0x3f3f3f3f,res=0;
      for(int i=n+1;i<=2*n;i++) ans=max(ans,(ll)f[i]*invt%p);
      for(int i=1;i<=n;i++) res+=a[i]*a[i]+b[i]*b[i];
      ans=res-2*ans-c;
      printf("%lld
    ",ans);
      //  printf("
    used space: %.2lfMB
    ",(&en-&be)/1048576.0);
      return 0;
    }
    
  • 相关阅读:
    信息的概念
    信息管理学基础
    第一章关键术语
    企业的转变
    国外的信息系统概念
    管理信息系统的基本功能
    人机交互设计-评价输入法-课下作业
    《构建之法》一
    27日进度
    26号进度
  • 原文地址:https://www.cnblogs.com/BruceW/p/12112616.html
Copyright © 2011-2022 走看看