Embiid  

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\n" , ans);
	} 
} 
posted on 2020-02-12 17:40  Embiid  阅读(204)  评论(0编辑  收藏  举报