2020 CCPC-Wannafly Winter Camp Day3 ---H. 火山哥的序列
题意:
解法:
显然是要枚举每个gcd来计算贡献的。我们考虑从大向小枚举gcd,这样可以避免重复计算。我们将gcd设为g。对于每个g,我们可以得到序列中它的倍数a[1],a[2]...,a[k-1],a[k]。
考虑删除区间,至少留下的情况可能是a[1]和a[2],a[1]和a[k],a[k-1]和a[k]。即对于1~a[1]位置的为左端点,右端点最多可以删到a[k-1]-1,对于a[1]+1-a[2]的数为左端点,最多可以山道a[k]-1,对于a[2]+1-n位置为左端点,可以删到n。这样我们可以维护每个位置最多删到哪,每次更新,多出来的区间就是以g为gcd的贡献。
#include <bits/stdc++.h>
#define ll long long
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int maxn = 2e5;
int n,m;
struct node {
ll sum;
int fm,sm,cnt;
}tree[4 * maxn + 11];
int pos[maxn + 11];
void push_up(int rt) {
tree[rt].sum = tree[lson].sum + tree[rson].sum;
if (tree[lson].fm < tree[rson].fm) {
tree[rt].fm = tree[lson].fm; tree[rt].cnt = tree[lson].cnt;
tree[rt].sm = min(tree[lson].sm , tree[rson].fm);
}
else if (tree[lson].fm > tree[rson].fm) {
tree[rt].fm = tree[rson].fm; tree[rt].cnt = tree[rson].cnt;
tree[rt].sm = min(tree[rson].sm , tree[lson].fm);
}
else {
tree[rt].fm = tree[rson].fm; tree[rt].cnt = tree[rson].cnt + tree[lson].cnt;
tree[rt].sm = min(tree[rson].sm , tree[lson].sm);
}
}
void upd(int rt,int val) {
if (tree[rt].fm >= val) return;
tree[rt].sum += 1ll * (val - tree[rt].fm) * tree[rt].cnt;
tree[rt].fm = val;
}
void push_down(int rt) {
upd(lson , tree[rt].fm);
upd(rson , tree[rt].fm);
}
void build(int rt,int l,int r) {
if (l == r) {
tree[rt].sum = 0;
tree[rt].fm = l - 1; tree[rt].sm = n + 1; tree[rt].cnt = 1;
return;
}
int mid = (l + r) >> 1;
build(lson , l , mid);
build(rson , mid + 1 , r);
push_up(rt);
}
void update(int rt,int l,int r,int al,int ar,int val) {
if (l > ar || r < al || tree[rt].fm >= val) return;
if (l >= al && r <= ar && tree[rt].sm >= val) { upd(rt , val); return; }
int mid = (l + r) >> 1;
push_down(rt);
update(lson , l , mid , al , ar , val);
update(rson , mid + 1 , r , al , ar , val);
push_up(rt);
}
int main(){
int t;
scanf("%d" , &t);
while (t--) {
scanf("%d" , &n);
m = 0;
for (int i = 1; i <= maxn; i++) pos[i] = 0;
for (int i = 1; i <= n; i++) {
int x;
scanf("%d" , &x);
pos[x] = i; m = max(m , x);
}
build(1 , 1, n);
ll ans = 0;
for (int g = m; g >= 1; g--) {
int a = n + 1; int b = n + 1;
int c = 0; int d = 0;
for (int i = g; i <= m; i += g){
if (!pos[i]) continue;
if (pos[i] <= a) { b = a; a = pos[i]; }
else if (pos[i] < b) b = pos[i];
if (pos[i] >= d) { c = d; d = pos[i]; }
else if (pos[i] > c) c = pos[i];
}
if (b == n + 1) continue;
ll num = tree[1].sum;
if (c == a) {
if (a > 1) update(1 , 1 , n , 1 , a - 1 , a - 1);
if (a + 1 <= b - 1) update(1 , 1 , n , a + 1 , b - 1 , b - 1);
if (b < n) update(1 , 1 , n , b + 1 , n , n);
}
else if (c == b) {
c = d;
update(1 , 1 , n , 1 , a , b - 1);
if (a + 1 <= b - 1) update(1 , 1 , n , a + 1 , b , c - 1);
update(1 , 1 , n , b + 1 , n , n);
}
else {
update(1 , 1 , n , 1 , a , c - 1);
update(1 , 1 , n , a + 1 , b , d - 1);
update(1 , 1 , n , b + 1 , n , n);
}
num = tree[1].sum - num;
ans += num * g;
}
printf("%lld
" , ans);
}
}