UPC-魔法序列(结论+枚举)

魔法序列

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
小E为了完成公主的任务,需排布魔法阵,从中获得法力。
简单起见,魔法阵可以看成一个长度为n的序列。序列从左到右都摆放了一张符卡,符卡有一个强度ai。法术的释放要每个元素相互配合,取得共鸣效果。一个由一些符卡组成的咒语的魔力值为这个咒语中所有符卡的强度的最大公因数乘以符卡的个数。
小E会从魔法阵中选择一段连续符卡区间[l,r](包括l,r端点),作为吟唱的咒语。她想知道,咒语最大的魔力值是多少。
输入
第一行一个整数n,表示符卡个数。
第二行n个正整数,第i个数表示符卡的强度ai。
输出
输出一个整数,表示最大的魔力值。
样例输入 Copy
5
30 60 20 20 20
样例输出 Copy
80
提示
样例解释
选择区间[2,5],其中gcd(60,20,20,20)=20,故魔力值为(5-2+1)*20=80。
在这里插入图片描述
有一个结论是

区间GCD收敛的很快,gcd的种类最多不超过nlogx(x是数的个数)

找不到证明了……

然后就可以进行优雅的暴力了~

枚举右端点,再每一个右端点内更新并记录gcd的值:记录每一个gcd出现的最靠左的位置,因为相同的gcd和相同的右端点,左端点越靠左,区间长度越长。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
 
const int maxn=1e5+100;
 
ll n,a[maxn];
 
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
 
map<ll,ll>mp1;
map<ll,ll>mp;
 
int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    ll res=0;
    for(int i=1;i<=n;i++){///枚举右端点
        mp=mp1;///用之前的区间更新这次对于右端点来说gcd的值和位置
        mp1.clear();
        res=max(res,a[i]);///区间长度是1
        if(!mp1.count(a[i])) mp1[a[i]]=i;
        for(auto it=mp.begin();it!=mp.end();it++){
            ll tmp=gcd(a[i],it->first);
            res=max(res,tmp*(i-it->second+1));
            
            if(!mp1.count(tmp)) mp1[tmp]=it->second;///记录这次对于下一个右端点的信息
            else mp1[tmp]=min(mp1[tmp],it->second);
            
        }
    }
    cout<<res<<endl;
    return 0;
}

类似的题:HDU5869
好短……

posted @ 2020-05-06 08:29  OvO1  阅读(54)  评论(0编辑  收藏  举报