Portal
这题前半部分挺简单的,但是后半部分非常巧妙。
首先考虑产生贡献的gcd对只有O(n)对。
首先若是一个前缀,那么一个后缀的最大gcd对可以扫一遍。
若一个后缀,也是一样的。
考虑一个非前缀而且非后缀的一个区间,对于每一个数,找到它倍数中最左边的,和倍数中最右边的。
那么现在贡献的gcd对只有3n对,每一个gcd对是对一个区间的每一个子区间产生贡献的。
这个问题也许有很多种方法解决,但是很难想到低于的复杂度。
根据Rose_max巨神的解法,将这个序列倍长,那么一个区间的子区间,就变成了包含两个节点而且长度<=n+1的区间。
例如区间是[l,r],倍长之后就会有一个对应的区间[l+n,r+n],一个合法的子区间当且仅当它包含[r,l+n]而且长度<=n+1。
可以这样想,在新的序列上[l,r]这个区间,相当于,[r-n,l]这个区间。
对于长度<=n+1这个限制我们可以先不管,那么合法的子区间就必须包含两个节点,我们把这一对节点按照右端点排个序,那么每次的操作相当于对一个前缀取max。前缀取max的操作可以直接在单调栈上面二分。
#include
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
const int N=200010,M=5000010,bound=200000;
int n,l,t,top,num;
int a[N],mmin[N],mmax[N];
bool tf[N];
vector d[N];
struct node{
int pos,x;
bool operator<(const node q)const{
return pos S;
set::iterator it,last;
void read(int&x){
char ch=getchar();x=0;
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
}
void solve(){
sort(q+1,q+1+t);S.clear();
int now=0;
long long ans=0,tot=0;
S.insert((node){n+1,0});
for(int r=n+1;r<=2*n;r++){
int l=r-n;
if(!S.empty()){
it=S.lower_bound((node){l-1,0});X=*it;
if(X.pos==l-1) S.erase(it);
ans-=X.x;
}
S.insert((node){l-1,1e9});
while(now=q[now].c) continue;
if(X.pos==q[now].x) S.erase(it);
S.insert((node){q[now].x,q[now].c});
ans+=1ll*(q[now].x-Y.pos)*(q[now].c-X.x);
while(q[now].c>=Y.x){
it=last;last--;S.erase(it);X=Y;Y=*last;
ans+=1ll*(X.pos-Y.pos)*(q[now].c-X.x);
}
}
S.erase(S.begin());
tot+=ans;
}
printf("%lld\n",tot);
}
void insert(int x,int y,int c){
q[++t]=(ques){y,x+n,c};
}
int main(){
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=bound;i>=1;i--)
for(int j=i;j<=bound;j+=i)
d[j].push_back(i);
for(int i=n;i>=1;i--)
for(int j=0;jmmat) mmat=to,insert(i+1,n,mmat);
}
memset(tf,false,sizeof(tf));
for(int i=0;i=2;i--){
int to=0;
for(int j=0;jmmat) mmat=to,insert(1,i-1,mmat);
}
solve();
}