最小环 java_luoguP6187 [NOI Online 提高组]最小环 贪心 记忆化

题目让我们把序列ai重新排列,使得环上任意两个距离为ki的数字乘积之和最大。 乘法与加法不同,把四个数分为两组分别计算,再求和。 比如1 2 3 4四个数 (1 + 2)+(3 + 4) ==  (1 + 3) + (2 + 4) (1 * 2) + (3 * 4)>(1*3)+(2*4) 对于乘法而言,大的数和大的数相乘,小的数和小的数相乘,能获取更大的运算结果。

c29799b489d053027fd04d1e6135778b.png

我们把这4个数,分成两部分。右上部分是11,9坐下部分是12,8这样子我们看到,最大的数,两侧是第2大和第3大的数,显然这样子,我们就不会“耽误”最大的数。

c94d93b0c08ad74655529a62f4d6e96c.png

我们刚刚注意到,如果n=12,k=3。我们似乎是要填4个独立的环。我们看到,每个颜色的环,都是独立按照刚刚的方法构造的。每个环之间没有任何影响,就是第一个环填的数是[12,9]第二个环填的数是[8,5]第三个环填的数是[4,1]

那么n,k还有刚刚我们每次构造,互相独立的“环”的数量关系是什么呢?环数=gcd(n,k)环长=n/gcd(n,k)

n是不变的。比如n=9,那当k=3或者k=6时,gcd(9,3)==gcd(9,6)==3,其实构造方法是完全一样的,所以我们以环长为下标,记忆化答案,

1 #include

2 #include

3 using namespacestd;4 typedef long longll;5 intn,m,k;6 ll ans,a[210000],f[210000];7 int gcd(int a,intb)8 {9 if (b == 0)10 returna;11 return gcd(b,a%b);12 }13 boolcmp(ll a,ll b)14 {15 return a >b;16 }17 intmain()18 {19 scanf("%d%d",&n,&m);20 for (int i = 1;i <= n;i++)21 scanf("%lld",&a[i]);22 sort(a + 1,a + n + 1,cmp);23 for (int i = 1;i <= m;i++)24 {25 ans = 0;26 scanf("%d",&k);27 if (k == 0)28 {29 for (int j = 1;j <= n;j++)30 ans += a[j] *a[j];31 printf("%lld\n",ans);32 continue;33 }34 int r = n /gcd(n,k);35 if (f[r] != 0)36 {37 printf("%lld\n",f[r]);38 continue;39 }40 for (int c = 1;c <= n /r;c++)41 {42 ans += a[(c - 1) * r + 1] * a[(c - 1) * r + 2];43 ans += a[(c - 1) * r + r - 1] * a[(c - 1) * r +r];44 for (int j = (c - 1) * r + 1;j <= (c - 1) * r + r - 2;j++)45 ans += a[j] * a[j + 2];46 }47 printf("%lld\n",ans);48 f[r] =ans;49 }50 return 0;51 }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值