【NOI OL #1】最小环

题目链接

我们来研究一下这个隔着一定距离形成的回路。设起点为$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;

}
View Code
原文地址:https://www.cnblogs.com/Hansue/p/12987827.html