description
solution:
又是一道小学奥数神题
从简单情况开始考虑
当(k=1),不妨考虑(n=6)的情况(其他类似)
最优情况是(lbrace1,3,5,6,4,2
brace)
作为一名结论人,大胆猜测构造解的方式:
首先(6)放在中间,然后交错地再6两边放(5,4,3,2,1)
为什么这样是最优的呢?
感性理解的话就是尽量让大的数和大的数相乘,小的数和小的数相乘
然后dfs验证小数据后发现都是对的,那就不妨假设这个结论是正确的吧
那如果(k=2)呢?还是先考虑(n=6)的情况
可以发现位置(2,4,6)和位置(1,3,5)是两个互不干扰的环
对于每个环,我们就按照之前(k=1)的情况构造就行
现在要考虑的是每个环究竟放置那些数
还是那个神必东西:尽量让大的数和大的数相乘,小的数和小的数相乘
于是相当于把所有数进行排序,然后按照顺序依次加入每一个环,最后把每个环的贡献加起来就是答案了
因为这样可以保证大的数和大的数在一起
但是如何求出环的个数呢?
手动模拟+感性理解可以发现答案就是({gcd(n,i)})其中(i)为询问
如果每次暴力做一遍显然会超时
于是我们可以预处理出(n)的所有约数的答案,询问的时候就可以(O(1))做了
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,a[N];
ll ans[N];
inline int read()
{
int s=0,w=1; char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s*w;
}
inline ll solve(vector<int>&v)
{
vector<int>nx;int sz=v.size();ll anss=0;
for(int i=0;i<sz;++i,++i)
nx.push_back(v[i]);
for(int i=sz-(sz&1?2:1);i>=0;--i,--i)
nx.push_back(v[i]);
for(int i=1;i<sz;++i)anss+=1ll*nx[i-1]*nx[i];
return anss+1ll*nx[sz-1]*nx[0];
}
inline void work(int x)
{
if(ans[x])return;
vector<int>v;
for(int i=1;i<=n;i+=x,v.clear())
{
for(int j=i;j<i+x;++j)v.push_back(a[j]);
ans[x]+=solve(v);
}
}
inline void pre()
{
sort(a+1,a+n+1);
int mx=sqrt(n+0.5)+1;
for(int i=1;i<=mx;++i)
if(n%i==0)work(i),work(n/i);
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i)a[i]=read();
pre();
while(m--)
{
int k=read();
printf("%lld
",ans[n/gcd(k,n)]);
}
return 0;
}