题目链接
我们来研究一下这个隔着一定距离形成的回路。设起点为$i$,距离为$k$,走了$t$步第一次回到$i$,即在$0sim n-1$编号内,有
$$egin{matrix} i+t imes k&equiv&i(mod n)\ t imes k&equiv&0(mod n) end{matrix}$$
又因为$t$最小,所以有$t imes k=p imes n=mathop{lcm}(k,n)$,所以
$$egin{matrix}t&=&dfrac{operatorname{lcm}(k,n)}{k}\ &=&dfrac{k imes n}{gcd(k,n) imes k}\ &=&dfrac{n}{gcd(k,n)}end{matrix}$$
也就是说,当设定距离为$k$时,会产生$gcd(k,n)$个长度为$dfrac{n}{gcd(k,n)}$的环。
然后我们尝试向这些环中填入数字,使得这些环内连续相乘的乘积的和最大。
首先考虑指定某个环内的数字$a_1sim a_n$,什么填法使得$a_1a_2+a_2a_3+a_3a_4+dots +a_{n-1}a_n+a_na+1$最大。
首先想到,从最大的数开始,依次贴着写下来,也即$a_1>a_2>a_n>a_3>a_n-1>a_4>dots$
来证明一波:
当$n=1,2,3$时不管怎样填都是一样的,命题也成立。(专业术语好像是叫做“命题是平凡的”?)
若当$n=k$时命题成立,则当$n=k+1$时,插入一个最大的数$a_0$,插入在哪里会使得乘积和最大?
当我们将$a_0$插入在$a_1a_2$之间时,乘积和增长了$a_0a_1+a_0a_2-a_1a_2$。
当我们将$a_0$插入在$a_xa_y$之间时,乘积和增长了$a_0a_x+a_0a_y-a_xa_y$。
接下来证明$a_0a_1+a_0a_2-a_1a_2ge a_0a_x+a_0a_y-a_xa_y$。
首先移项得到$a_0(a_1+a_2-a_x-a_y)ge a_1a_2-a_xa_y$。
然后等式左边$ge a_1(a_1+a_2-a_x-a_y)=a_1a_1+a_1a_2-a_1a_x-a_1a_y$。
于是只要$a_1a_1+a_1a_2-a_1a_x-a_1a_yge a_1a_2-a_xa_y$成立,就会有原不等式成立。
这里移项,发现$a_1a_1+a_xa_yge a_1a_x+a_1a_y$可以用排序不等式证明。于是原不等式成立。
所以当$n=k+1$时命题成立,也即对所有正整数$n$,命题都成立。归纳证明完毕。
然后就是分组的问题了。这个挺显然的直接从大到小分过去。
证明……思考一下,微扰法?也就是随便抽两个不同组的位置换一下,发现整体和会变小,说明换不得……这个证明是对的吗?
或者是换个思路:
$$2(sum a_i^2 -sum a_ia_j)=sum(a_i-a_j)^2$$
也即“连续乘积和最大”$longrightarrow$“相邻差平方和最小”
然后也很显然是每次取连续的一段放进同一个环里,而且为了不产生断层,从最大的数开始取。这个也证不动了。算了,放弃了。
最后,考虑预处理出所有可能的$gcd(n,k)$的情况,也所有就是小于$n/2$的约数。约数的规模是$d(n)$的(总之比$sqrt{n}$小),比询问数小多了。
注意运算爆int的问题,然后特判一下$k=0$的情况
代码(100分):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<map> #include<set> #define IL inline #define RG register #define _1 first #define _2 second using namespace std; typedef long long LL; const int N=2e5; int n,m; LL a[N+3],ans[N+3]; IL void init(){ sort(a+1,a+n+1,greater<int>()); memset(ans,0,sizeof ans); for(int i=1;i<=n;i++) ans[0]+=a[i]*a[i]; } IL int gcd(int a,int b){ if(a<b) swap(a,b); int r=a%b; while(r>0){ a=b; b=r; r=a%b; } return b; } IL void sol(){ int k; scanf("%lld",&k); if(k==0){ printf("%lld ",ans[k]); return ; } k=gcd(n,k); if(ans[k]!=0){ printf("%lld ",ans[k]); return ; } int p=n/k; ans[k]=0; for(int i=0;i<k;i++){ int s=i*p; ans[k]+=a[s+1]*a[s+2]; for(int j=1;j+2<=p;j+=2) ans[k]+=a[s+j]*a[s+j+2]; for(int j=2;j+2<=p;j+=2) ans[k]+=a[s+j]*a[s+j+2]; ans[k]+=a[s+p-1]*a[s+p]; } printf("%lld ",ans[k]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); init(); while(m--) sol(); return 0; }