P9032 [COCI2022-2023#1] Neboderi

发布时间 2023-12-21 15:14:52作者: MrcFrst

LuoguBlog

原题传送门


题意

给定 \(a\) 序列,一个区间的权值为区间 \(\gcd\) 乘上区间和,求长度 \(\ge k\) 的区间的最大权值。

\(1\le n,V\le 10^6\)


题解

区间 \(\gcd\) 相较于区间和更难维护,考虑枚举 \(\gcd\)

记当前钦定的 \(\gcd\)\(x\),那么我们选择的区间一定是极长的包含 \(x\) 倍数的连续段,否则从长度限制上和区间和上都会更劣。

直接枚举所有 \(x\) 然后 \(O(n)\) 扫一遍的 \(O(nV)\) 复杂度是不能接受的,但是考虑到此处 \(V\le 10^6\),意味着 \(d(a_i)\le 240\),这里 \(d\) 为正因数个数。

所以我们维护每个 \(x\) 当前的连续段区间,扫一遍所有 \(a_i\) 并枚举 \(a_i\) 的所有因数 \(d\),然后判断 \(d\) 是否在这里产生了新的连续段,如果是就计算上个连续段的答案并记录新的连续段,否则更新连续段右端点为 \(i\) 即可。

时间复杂度为 \(O(\sum d(a_i))\),最坏情况下达到 \(O(240n)\),大概要跑 \(1.5s\)

\(\texttt{Code}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define re register
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
char buf[1<<22],*p1=buf,*p2=buf;
const int N=1e6+10;
int n,k,a[N],mx;
ll s[N],ans;
#define pb emplace_back
vector<int>d[N];
#define pii pair<int,int>
#define mkp make_pair
pii pos[N];
il int read(){
    re int x=0;re char c=getchar(),f=0;
    while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f?-x:x;
}
#define l(x) pos[x].first
#define r(x) pos[x].second
int main(){
    // freopen("intelligence.in","r",stdin);
    // freopen("intelligence.out","w",stdout);
    n=read(),k=read();
    for(re int i=1;i<=n;i++)a[i]=read(),mx=max(mx,a[i]),s[i]=s[i-1]+a[i];
    const int V=mx;
    for(re int i=1;i*i<=V;i++){
    	d[i*i].pb(i);
    	for(re int j=i+1;j*i<=V;j++)d[i*j].pb(i),d[i*j].pb(j);
	}
    for(re int i=1;i<=V;i++)r(i)=-1;
    for(int x:d[a[1]])pos[x]=mkp(1,1);
    for(re int i=2;i<=n;i++)
        for(int x:d[a[i]]){
            if(r(x)==i-1)r(x)=i;
            else{
                if(r(x)-l(x)+1>=k)ans=max(ans,x*(s[r(x)]-s[l(x)-1]));
                pos[x]=mkp(i,i);
            }
        }
    for(re int i=1;i<=V;i++)
		if(r(i)-l(i)+1>=k)ans=max(ans,i*(s[r(i)]-s[l(i)-1]));
    cout<<ans;
    return 0;
}