看见$a_i\leq 200000$和gcd,就大概知道是要枚举gcd也就是答案了...

  因为答案是max,可以发现我们很容易算出<=i的答案,但是很难求出单个i的答案,所以我们可以运用差分的思想。

  $H[i]$表示$f(l,r)<=i$的$(l,r)$对数,显然这个是随i增大而单调不降的,考虑怎么计算出这个。

  $next[l]$表示满足$f(l,r)<=i$的最小的$r$,则有$H[i]=\sum_{l=1}^{n}n-next[l]+1$。显然$next[l]$也会随着$i$变小而单调不降,并且$next$数组本身也是单调不降的,于是我们可以从大到小枚举$i$。$i=max(a[i])$的时候显然有$next[i]=i$,接下来只要考虑每次从$i$变成$i-1$的时候$next$数组怎么变化。

  用一个vector $v[i]$来存下约数里有$i$的数的下标,并且单调递增。设$v[i]$里有下标$x_1,x_2,x_3,...,x_m$,则从$i$变为$i-1$的时候,任何一个$[l,r]$至少包含$m-1$个$x_j$,所以$[x_2+1,n]$这段区间的$next[l]$应该全改为$n+1$,$[x_1+1,x_2]$这段区间的$next[l]$应该改为$max(next[l],x_m)$,$[1,x_1]$这段区间的$next[l]$应该改为$max(next[l],x_{m-1})$,这个可以用线段树来实现。

  怎么实现呢?刚才我们提到过$next[l]$单调不降,有了这个性质就很好实现了。

  每个节点维护$mn$和$mx$表示这个区间里的最小的$next$和最大的$next$,如果$mn \geq delta$,那就不用再递归这个区间了,如果$mx<delta$,那么直接给这个区间打标记,这么做就能找到需要改的区间了。

  然后求出$H[i]$就完了...

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=;
struct poi{int mx, mn, delta; ll sum;}tree[maxn<<];
int n, a[maxn], pos[maxn], mx;
ll ans, H[maxn];
vector<int>v[maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-' && (f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline void change(int x, int l, int r, int delta)
{
tree[x].mn=tree[x].mx=delta;
tree[x].sum=1ll*(r-l+)*delta;
tree[x].delta=delta;
}
inline void up(int x)
{
tree[x].mn=min(tree[x<<].mn, tree[x<<|].mn);
tree[x].mx=max(tree[x<<].mx, tree[x<<|].mx);
tree[x].sum=tree[x<<].sum+tree[x<<|].sum;
}
inline void down(int x, int l, int r)
{
if(!tree[x].delta) return;
int mid=(l+r)>>;
change(x<<, l, mid, tree[x].delta);
change(x<<|, mid+, r, tree[x].delta);
tree[x].delta=;
}
void build(int x, int l, int r)
{
if(l==r) {tree[x].mn=tree[x].mx=tree[x].sum=l; return;}
int mid=(l+r)>>;
build(x<<, l, mid); build(x<<|, mid+, r);
up(x);
}
void update(int x, int l, int r, int cl, int cr, int delta)
{
if(tree[x].mn>=delta) return;
down(x, l, r);
if(cl<=l && r<=cr && tree[x].mx<delta) {change(x, l, r, delta); return;}
int mid=(l+r)>>;
if(cl<=mid) update(x<<, l, mid, cl, cr, delta);
if(cr>mid) update(x<<|, mid+, r, cl, cr, delta);
up(x);
}
int main()
{
read(n);
for(int i=;i<=n;i++) read(a[i]), pos[a[i]]=i, mx=max(mx, a[i]);
for(int i=;i<=mx;i++)
{
for(int j=i;j<=mx;j+=i)
if(pos[j]) v[i].push_back(pos[j]);
sort(v[i].begin(), v[i].end());
}
build(, , n);
for(int i=mx;~i;i--)
{
H[i]=1ll*n*n-tree[].sum+n;
int m=v[i].size();
if(m<=) continue;
update(, , n, v[i][]+, n, n+);
update(, , n, v[i][]+, v[i][], v[i][m-]);
update(, , n, , v[i][], v[i][m-]);
}
for(int i=;i<=mx;i++) ans+=1ll*i*(H[i]-H[i-]);
printf("%I64d\n", ans);
}

Codeforces 671C. Ultimate Weirdness of an Array(数论+线段树)的更多相关文章

  1. Codeforces 671C - Ultimate Weirdness of an Array(线段树维护+找性质)

    Codeforces 题目传送门 & 洛谷题目传送门 *2800 的 DS,不过还是被我自己想出来了 u1s1 这个 D1C 比某些 D1D 不知道难到什么地方去了 首先碰到这类问题我们肯定考 ...

  2. codeforces 671C Ultimate Weirdness of an Array 线段树+构造

    题解上说的很清楚了,我照着写的,表示膜拜题解 然后时间复杂度我觉得应该是O(nlogn),虽然常数略大,预处理和倒着扫,都是O(nlogn) #include <stdio.h> #inc ...

  3. CodeForces 671C - Ultimate Weirdness of an Array

    题意: 给以一个定义, F(l, r) 的值表示序列 A[1:n]的子序列 A[1....(l-1),(r+1)...n] 之中 任意两个数的最大公约数的最大值. 求 Sum=∑i=1N∑j=1N(F ...

  4. CodeForces Round #179 (295A) - Greg and Array 一个线段树做两次用

    线段树的区间更新与区间求和...一颗这样的线段树用两次... 先扫描1~k...用线段树统计出每个操作执行的次数... 那么每个操作就变成了 op. l  , op.r , op.c= times* ...

  5. 【CodeForces】671 C. Ultimate Weirdness of an Array

    [题目]C. Ultimate Weirdness of an Array [题意]给定长度为n的正整数序列,定义一个序列的价值为max(gcd(ai,aj)),1<=i<j<=n, ...

  6. [Codeforces 464E] The Classic Problem(可持久化线段树)

    [Codeforces 464E] The Classic Problem(可持久化线段树) 题面 给出一个带权无向图,每条边的边权是\(2^{x_i}(x_i<10^5)\),求s到t的最短路 ...

  7. CF671C. Ultimate Weirdness of an Array

    n<=200000个<=200000的数问所有的f(i,j)的和,表示去掉区间i到j后的剩余的数字中任选两个数的最大gcd. 数论日常不会.. 先试着计算一个数组:Hi表示f(l,r)&l ...

  8. codeforces 482B. Interesting Array【线段树区间更新】

    题目:codeforces 482B. Interesting Array 题意:给你一个值n和m中操作,每种操作就是三个数 l ,r,val. 就是区间l---r上的与的值为val,最后问你原来的数 ...

  9. codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)(两种方法)

    In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation F1 ...

随机推荐

  1. github如何删除仓库中文件夹

    github项目中,经常大家更新.添加都非常熟悉,但是如果想要删掉一个文件夹,很多人就不知道怎么操作了. 网上查了一些方法,大部分都是删除仓库重新上传,这样肯定是不行的,首先不说任务量大,而且删除仓库 ...

  2. flask ssti python2和python3 注入总结和区别

    总结一下flask ssti的注入语句 代码 import uuid from flask import Flask, request, make_response, session,render_t ...

  3. VS2013只显示会附加到进程,无法启动调试

    今天在使用VS2013的时候,打开突然发现,只显示附加到进程,无法进行调试,调试位置显示灰色,到网上各处寻求答案,本以为是个大问题,没想到只是个小问题.主要原因只是后台开太多东西了,导致VS2013运 ...

  4. 如何停止AAD服务

    Connect-MsolService (Get-MSOLCompanyInformation).DirectorySynchronizationEnabled 用这个命令查看是enable还是Dis ...

  5. C# ArcFace 免费人脸识别 2.0 demo

    **配置过程:** 1. 到[虹软官网](https://ai.arcsoft.com.cn/index.htm?utm_source=csdn&utm_medium=referral)下载S ...

  6. Paper Reading - Deep Visual-Semantic Alignments for Generating Image Descriptions ( CVPR 2015 )

    Link of the Paper: https://arxiv.org/abs/1412.2306 Main Points: An Alignment Model: Convolutional Ne ...

  7. 2018-2019-20172329 《Java软件结构与数据结构》第四周学习总结

    2018-2019-20172329 <Java软件结构与数据结构>第四周学习总结 经过这样一个国庆节的假期,心中只有一个想法,这个国庆假期放的,不如不放呢!! 教材学习内容总结 < ...

  8. OGNL动态实现result

    OGNL就是struts.xml文件中的<result>通过get()方法,动态获取action类中的变量 <struts> <package name="de ...

  9. lintcode-389-判断数独是否合法

    389-判断数独是否合法 请判定一个数独是否有效. 该数独可能只填充了部分数字,其中缺少的数字用 . 表示. 注意事项 一个合法的数独(仅部分填充)并不一定是可解的.我们仅需使填充的空格有效即可. 说 ...

  10. (一)java数据类型图

    ┏数值型━┳━整数型:byte short int long               ┏基本数据类型━━┫              ┗━浮点型:float double              ...