解题:AHOI2017/HNOI2017 礼物

题面

先不管旋转操作,只考虑增加亮度这个操作。显然这个玩意的影响是相对于$x,y$固定的,所以可以枚举增加的亮度然后O(1)算出来。为了方便我们把这个操作换种方法表示,只让一个手环改变$[-m,m]$中的一个亮度$k$。这样把$\sum\limits_{i=1}^n(x_i-y_i+k)^2$拆完以后发现只有$\sum\limits_{i=1}^n 2x_iy_i$这个玩意跟$x,y$的顺序有关,于是先扫一遍把其他的求出来

然后考虑旋转的操作,环上问题有个很经典的操作:断环为链。注意到这里是两个多项式的同一项向答案的同一项做贡献,相当于差值一定,于是就是套路的把一个多项式反过来,然后卷就完事了

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<cstring>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N=400005,M=30;
 8 const double pai=acos(-1);
 9 struct cpx
10 {
11     double x,y;
12 }a[N],b[N];
13 cpx operator + (cpx a,cpx b)
14 {
15     return (cpx){a.x+b.x,a.y+b.y};
16 }
17 cpx operator - (cpx a,cpx b)
18 {
19     return (cpx){a.x-b.x,a.y-b.y};
20 }
21 cpx operator * (cpx a,cpx b)
22 {
23     double x1=a.x,x2=b.x,y1=a.y,y2=b.y;
24     return (cpx){x1*x2-y1*y2,x1*y2+x2*y1};
25 }
26 double Sin[M],Cos[M];
27 long long outp,cal,sm1,sm2;
28 int n,m,nm,rd,rev[N],lgg[N],ans[N];
29 void read(int &x)
30 {
31     x=0; char ch=getchar();
32     while(!isdigit(ch))
33         ch=getchar();
34     while(isdigit(ch))
35         x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
36 }
37 void prework()
38 {
39     register int i;
40     read(n),read(m);
41     for(i=1;i<=n;i++)
42     {
43         read(rd),sm1+=rd*rd,sm2+=rd;
44         a[i].x=a[i+n].x=rd;
45     }
46     for(i=1;i<=n;i++)
47     {
48         read(rd),sm1+=rd*rd,sm2-=rd;
49         b[n-i+1].x=rd;
50     }
51     cal=outp=1e9,nm=n,m=3*n,n=1,lgg[1]=0; while(n<=m) n<<=1;
52     for(i=2;i<=n;i++)
53         lgg[i]=lgg[i>>1]+1;
54     for(i=1;i<=n;i++) 
55         rev[i]=(rev[i>>1]>>1)+(i&1)*(n>>1);
56     for(i=1;i<=24;i++) 
57         Sin[i]=sin(2*pai/(1<<i)),Cos[i]=cos(2*pai/(1<<i));
58 }
59 void transform(cpx *c,int t)
60 {
61     register int i,j,k;
62     for(i=0;i<n;i++)
63         if(rev[i]>i) swap(c[i],c[rev[i]]);
64     for(i=2;i<=n;i<<=1)
65     {
66         int len=i>>1;
67         cpx omg={Cos[lgg[i]],Sin[lgg[i]]*t};
68         for(j=0;j<n;j+=i)
69         {
70             cpx ori={1,0},tmp;
71             for(k=j;k<j+len;k++,ori=ori*omg)
72                 tmp=ori*c[k+len],c[k+len]=c[k]-tmp,c[k]=c[k]+tmp;
73         }
74     }
75 }
76 int main()
77 {
78     register int i; prework();
79     transform(a,1),transform(b,1);
80     for(i=0;i<n;i++) a[i]=a[i]*b[i];
81     transform(a,-1);
82     for(i=0;i<=2*nm;i++) ans[i]=(int)(a[i].x/n+0.5);
83     for(i=-m;i<=m;i++) cal=min(cal,sm1+1ll*i*i*nm+2ll*sm2*i);
84     for(i=1;i<=nm;i++) outp=min(outp,cal-2ll*ans[i+nm]);
85     printf("%lld",outp);
86     return 0;
87 }
View Code

 

转载于:https://www.cnblogs.com/ydnhaha/p/10147976.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值