[BZOJ 4827][Hnoi2017]礼物

4827: [Hnoi2017]礼物

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1091  Solved: 748
[Submit][Status][Discuss]

Description

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

Input

输入数据的第一行有两个数n, m,代表每条手环的装饰物的数量为n,每个装饰物的初始 亮度小于等于m。
接下来两行,每行各有n个数,分别代表第一条手环和第二条手环上从某个位置开始逆时 针方向上各装饰物的亮度。
1≤n≤50000, 1≤m≤100, 1≤ai≤m

Output

输出一个数,表示两个手环能产生的最小差异值。
注意在将手环改造之后,装饰物的亮度 可以大于 m。

Sample Input

5 6
1 2 3 4 5
6 3 3 4 5

Sample Output

1
【样例解释】
需要将第一个手环的亮度增加1,第一个手环的亮度变为: 2 3 4 5 6 旋转一下第二个手环。对于该样例,是将第
二个手环的亮度6 3 3 4 5向左循环移动 2017-04-15 第 6 页,共 6 页 一个位置,使得第二手环的最终的亮度为
:3 3 4 5 6。 此时两个手环的亮度差异值为1。

题解

环上的东西我们按照套路倍长解决.

然后我们来看那个表达式: $\sum\limits_{i=1}^n(x_i-y_i)^2$...

好像在哪见过? 是哪呢?

卷积与字符串

没错就是你! 字符串匹配!

那么好了我们翻转其中一个串然后拆拆式子看看能得到什么:

$$
\begin{aligned}
c_k&=\sum_{i=0}^k(a_i-b_{k-i})^2 \\
&=\sum_{i=0}^ka_i^2-2a_ib_{k-i}+b_{k-i}^2 \\
&=\sum_{i=0}^ka_i^2+\sum_{i=0}^kb_{k-i}^2-2\sum_{i=0}^ka_ib_{k-i}\\
&=\sum_{i=0}^ka_i^2+\sum_{i=0}^kb_i^2-2\sum_{i=0}^ka_ib_{k-i}
\end{aligned}
$$

而$\sum\limits_{i=0}^ka_ib_{k-i}$ 显然是个卷积, 剩下两个和式是前缀和可以预处理.

那么我们只要有了 $a$ 和 $b$ , 我们就可以在 $O(n\log n)$ 时间内算出所有位置的差异值了...吗?

然而并不是这样的.

我们在字符串匹配问题中, 高位补 $0$ 相当于补的是通配符, 不会对答案作出贡献. 但这次 $0$ 可就是字符了, 如果按照上面的式子算的话会把前 $k$ 个字符全都对位计算贡献. ($(a-0)^2$可是有贡献的).

我们设倍长并翻转的串是 $a$, 另一个串是 $b$, 那么实际上的式子应该是:

$$
\begin{aligned}
c_k&=\sum_{i=0}^{n-1}(a_{k-i}-b_i)^2 \\
&=\sum_{i=0}^{n-1}a_{k-i}^2-2a_{k-i}b_i+b_i^2 \\
&=\sum_{i=0}^{n-1}a_{k-i}^2+\sum_{i=0}^{n-1}b_i^2-2\sum_{i=0}^{n-1}a_{k-i}b_i
\end{aligned}
$$

前面两个式子依然是前缀和即可处理, 后面的卷积式就可以通过高位补 $0$ 来避免多余计算了.

可是这 $a$ 和 $b$ 可能会变...这就比较蠢...直接 $O(mn\log n)$ 跑铁定是过不去的...

我们再来拆式子.

那么我们首先来考虑给 $a$ 上加东西的情况. 设我们给 $a$ 整体加一个 $d$ , 则:

$$
\begin{aligned}
c_k&=\sum_{i=0}^{n-1}((a_{k-i}+d)-b_i)^2 \\
&=\sum_{i=0}^{n-1}(a_{k-i}+d)^2-2a_{k-i}b_i+b_i^2 \\
&=\sum_{i=0}^{n-1}(a_{k-i}+d)^2+\sum_{i=0}^{n-1}b_i^2-2\sum_{i=0}^{n-1}(a_{k-i}+d)b_i\\
&=\sum_{i=0}^{n-1}(a_{k-i}+d)^2+\sum_{i=0}^{n-1}b_i^2-2\sum_{i=0}^{n-1}a_{k-i}b_i+\sum_{i=0}^{n-1}db_i \\
&=\sum_{i=0}^{n-1}(a_{k-i}+d)^2+\sum_{i=0}^{n-1}b_i^2-2\sum_{i=0}^{n-1}a_{k-i}b_i+d\sum_{i=0}^{n-1}b_i
\end{aligned}
$$

于是原来的卷积式还在, 剩下三个前缀和式都可以 $O(n)$ 随便重新算, 直接枚举 $d$ 然后暴力就可以了

给 $b$ 上加东西的情况比较辣手, 因为直接给所有 $b$ 都加值是假的...这样会把防止产生贡献的系数 $0$ 变成非 $0$ 值, 然后就会爆炸...

所以我们再搞一搞式子

\begin{aligned}
c_k&=\sum_{i=0}^{n-1}(a_{k-i}-(b_i+d))^2 \\
&=\sum_{i=0}^{n-1}a_{k-i}^2-2a_{k-i}(b_i+d)+(b_i+d)^2 \\
&=\sum_{i=0}^{n-1}a_{k-i}^2+\sum_{i=0}^{n-1}(b_i+d)^2-2\sum_{i=0}^{n-1}a_{k-i}(b_i+d)\\
&=\sum_{i=0}^{n-1}a_{k-i}^2+\sum_{i=0}^{n-1}(b_i+d)^2-2\sum_{i=0}^{n-1}a_{k-i}b_i+da_{k-i}\\
&=\sum_{i=0}^{n-1}a_{k-i}^2+\sum_{i=0}^{n-1}(b_i+d)^2-2\sum_{i=0}^{n-1}a_{k-i}b_i+d\sum_{i=0}^{n-1}a_{k-i}\\
\end{aligned}

好了, 又是三个前缀和一个和 $d$ 无关的卷积.

FFT/NTT直接跑就行了

参考代码

  1 #include <bits/stdc++.h>
  2 
  3 const int G=3;
  4 const int DFT=1;
  5 const int IDFT=-1;
  6 const int MAXN=270000;
  7 const int MOD=998244353;
  8 
  9 int n;
 10 int m;
 11 int bct;
 12 int bln=1;
 13 int a[MAXN];
 14 int b[MAXN];
 15 int c[MAXN];
 16 int na[MAXN];
 17 int nb[MAXN];
 18 int sa[MAXN];
 19 int sb[MAXN];
 20 int sa2[MAXN];
 21 int sb2[MAXN];
 22 int rev[MAXN];
 23 
 24 int Pow(int,int,int);
 25 void NTT(int*,int,int);
 26 
 27 int main(){
 28     scanf("%d%d",&n,&m);
 29     for(int i=0;i<n;i++)
 30         scanf("%d",a+i);
 31     for(int i=n;i<2*n;i++)
 32         a[i]=a[i-n];
 33     std::reverse(a,a+2*n);
 34     for(int i=0;i<2*n;i++){
 35         na[i]=a[i];
 36         sa[i]=(i>0?sa[i-1]:0)+a[i];
 37         sa2[i]=(i>0?sa2[i-1]:0)+a[i]*a[i];
 38     }
 39     for(int i=0;i<n;i++){
 40         scanf("%d",b+i);
 41         nb[i]=b[i];
 42         sb[i]=(i>0?sb[i-1]:0)+b[i];
 43         sb2[i]=(i>0?sb2[i-1]:0)+b[i]*b[i];
 44     }
 45     for(int i=n;i<2*n;i++){
 46         sb[i]=sb[i-1];
 47         sb2[i]=sb2[i-1];
 48     }
 49     while(bln<3*n){
 50         bln<<=1;
 51         ++bct;
 52     }
 53     //printf("bln=%d\n",bln);
 54     for(int i=0;i<bln;i++)
 55         rev[i]=(rev[i>>1]>>1)|((i&1)<<(bct-1));
 56     NTT(na,bln,DFT);
 57     NTT(nb,bln,DFT);
 58     for(int i=0;i<bln;i++)
 59         c[i]=1ll*na[i]*nb[i]%MOD;
 60     NTT(c,bln,IDFT);
 61     int ans=INT_MAX;/*
 62     for(int i=0;i<2*n;i++)
 63         printf("c[%d]=%d\n",i,c[i]);*/
 64     for(int d=0;d<=m;d++){ // Change A
 65         for(int i=0;i<2*n;i++){
 66             sa[i]=(i>0?sa[i-1]:0)+a[i]+d;
 67             sa2[i]=(i>0?sa2[i-1]:0)+(a[i]+d)*(a[i]+d);
 68         }
 69         for(int i=n-1;i<2*n-1;i++){
 70             int sum=sa2[i]+sb2[i];
 71             sum-=2*(c[i]+d*sb[i]);
 72             if(i>=n)
 73                 sum-=sa2[i-n];
 74             //printf("d=%d pos %d: sa2=%d sb2=%d c=%d sum=%d\n",d,i,sa2[i],sb2[i],c[i],sum);
 75             ans=std::min(ans,sum);
 76         }
 77     }
 78     //printf("%d\n",ans);
 79     for(int i=0;i<2*n;i++){
 80         sa[i]=(i>0?sa[i-1]:0)+a[i];
 81         sa2[i]=(i>0?sa2[i-1]:0)+a[i]*a[i];
 82     }
 83     for(int d=0;d<=m;d++){ // Change B
 84         for(int i=0;i<n;i++){
 85             sb[i]=(i>0?sb[i-1]:0)+b[i]+d;
 86             sb2[i]=(i>0?sb2[i-1]:0)+(b[i]+d)*(b[i]+d);
 87         }
 88         for(int i=n;i<2*n;i++){
 89             sb[i]=sb[i-1];
 90             sb2[i]=sb2[i-1];
 91         }
 92         for(int i=n-1;i<2*n-1;i++){
 93             int sum=sa2[i]+sb2[i];
 94             sum-=2*(c[i]+d*(sa[i]-(i>=n?sa[i-n]:0)));
 95             if(i>=n)
 96                 sum-=sa2[i-n];
 97             ans=std::min(ans,sum);
 98         }
 99     }
100     printf("%d\n",ans);
101     return 0;
102 }
103 
104 void NTT(int* a,int len,int opt){
105     for(int i=0;i<len;i++)
106         if(rev[i]>i)
107             std::swap(a[rev[i]],a[i]);
108     for(int i=1;i<len;i<<=1){
109         int step=i<<1;
110         int wn=Pow(G,(MOD-1+opt*(MOD-1)/step)%(MOD-1),MOD);
111         for(int j=0;j<len;j+=step){
112             int w=1;
113             for(int k=0;k<i;k++,w=1ll*w*wn%MOD){
114                 int x=a[j+k];
115                 int y=1ll*w*a[j+k+i]%MOD;
116                 a[j+k]=(x+y)%MOD;
117                 a[j+k+i]=(x-y+MOD)%MOD;
118             }
119         }
120     }
121     if(opt==IDFT){
122         int inv=Pow(len,MOD-2,MOD);
123         for(int i=0;i<len;i++)
124             a[i]=1ll*a[i]*inv%MOD;
125     }
126 }
127 
128 inline int Pow(int a,int n,int p){
129     int ans=1;
130     while(n>0){
131         if(n&1)
132             ans=1ll*a*ans%p;
133         a=1ll*a*a%p;
134         n>>=1;
135     }
136     return ans;
137 }
BZOJ 4827

日常图包

 

posted @ 2019-01-04 16:00  rvalue  阅读(225)  评论(0编辑  收藏  举报