P6187 [NOI Online #1 提高组] 最小环

题目大意

给定一个长度为 n 的正整数序列 ai​,下标从 1 开始编号。我们将该序列视为一个首尾相邻的环,更具体地,对于下标为 i, (ij) 的两个数 ai​, aj​,它们的距离为 min(ji,i+nj)

现在再给定 m 个整数 k1​, k2​,..., km​,对每个 ki(i=1, 2,..., m),你需要将上面的序列 ai​ 重新排列,使得环上任意两个距离为 ki​ 的数字的乘积之和最大。

思路

  1. 如果gcd(n,ki)1则一定会形成多个部分

  2. k=1时,显然应该大的挨着大的,以最大的为中心,向两侧依次减少。

  3. k1gcd(n,k)1时,我们考虑将k=1时的代价断开,每一段长nk,一共有k段。

例如: 6 5 4 3 2 1
在k=1时为:6×5 6×4 5×3 4×2 3×1 1×2
在k=2时为:6×5 6×4 5×4 3×2 2×1 3×1
不难发现,若在i和i+1处断开,则对答案的贡献为
-(a[i]*a[i+2]+a[i-1]*a[i+1])+(a[i]*a[i-1]+a[i+1]*a[i+2])
  1. 预处理出来,然后乱搞即可

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>
#define LL long long
#define N 200010
using namespace std;
LL n,m,a[N],ans[N];
LL read(){
LL x=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(LL)(ch-'0');ch=getchar();}
return x*h;
}
LL gcd(LL a,LL b){
return b==0 ? a:gcd(b,a%b);
}
void gans(LL k){
LL jp=n/k;
ans[k]=ans[1];
for(LL i=n-jp;i;i-=jp){
ans[k]-=(a[i+2]*a[i]+a[i-1]*a[i+1]);
ans[k]+=(a[i]*a[i-1]+a[i+1]*a[i+2]);
}
return ;
}
void solve(){
// solve q=0
for(LL i=1;i<=n;i++){
ans[0]+=(a[i]*a[i]);
}
// solve q=1
ans[1]+=a[n]*a[n-1];
for(LL i=n;i>2;i-=2){
ans[1]+=a[i]*a[i-2];
}
for(LL i=n-1;i>2;i-=2){
ans[1]+=a[i]*a[i-2];
}
ans[1]+=a[1]*a[2];
// solve other
for(LL i=2;i<=sqrt(n);i++){
if(n%i!=0)continue;
// solve i
gans(i);
// solve n/i
if(n/i<=n/2)gans(n/i);
}
}
int main(){
n=read(); m=read();
for(LL i=1;i<=n;i++)a[i]=read();
sort(a+1,a+n+1);
solve();
for(LL i=1;i<=m;i++){
LL x=read();
//注意特判!!!
if(n==1){
printf("%lld\n",a[1]*a[1]);
continue;
}
if(x==0)printf("%lld\n",ans[0]);
else printf("%lld\n",ans[gcd(x,n)]);
}
return 0;
}
posted @   Charisk_FOD  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示