ccpc2021 网络选拔赛 H. GCD on Sequence (线段树)

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=7107

经典统计所有 ((l,r)) 区间的问题,所以固定右端点进行考虑

观察到如果固定右端点 (r)(l)(r) 区间内的 (gcd) 最大值随着 (l) 从右到左而逐渐变大
同时,如果区间 ([i,j]) 内的最大公因数为 (x),则 ([i,j]) 内一定至少存在两个 (x) 的倍数

于是我们考虑从大到小枚举 (gcd),同时维护每个右端点当前未确定答案的最左边的位置(([l,r])(gcd) 越大的 (l) 越靠左),于是相当于每次遍历 (gcd) 倍数所在的位置 (g[i]),将 ([g[i+1],n]) 内的答案与 (g[i]+1) 取最大值(当前已确定固定的 (r) 的左端 (l)([1,g[i]])),可以使用 (Segment tree beats) 解决

又可以注意到,对于将要进行区间取最大值操作的 ([g[i+1],n]) 这段区间,左端点固定,则 (r) 越大,区间中最大 (gcd) 越大,所以 ([g[i+1],n]) 区间内维护的最左端的位置也是单调递增的,所以只需要在线段树内二分出这段区间中最后一个需要更新的位置,使用区间覆盖更新即可,每个 (gcd) 操作前后线段树维护的最左端位置的和的差(每个固定右端点更新的区间长度和)即为该 (gcd) 的答案

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;

const int maxn = 100010; 

int T, n;
int a[maxn], p[maxn];
ll ans[maxn];

vector<int> fac[maxn];

struct Node{
	int tag, mi;
	ll sum;
}t[maxn<<2];

void pushup(int i){
	t[i].mi = min(t[i<<1].mi, t[i<<1|1].mi);
	t[i].sum = t[i<<1].sum + t[i<<1|1].sum; 
}

void build(int i, int l, int r){
	if(l == r){
		t[i].tag = 0;
		t[i].mi = 1;
		t[i].sum = 1ll;
		return;
	}
	int mid = (l+r) >> 1;
	build(i<<1, l, mid); build(i<<1|1, mid+1, r);
	pushup(i); 
}

void pushdown(int i, int l, int r){
	if(t[i].tag){
		t[i<<1].tag = t[i<<1|1].tag = t[i].tag;
		int mid = (l+r) >> 1;
		t[i<<1].mi = t[i].tag;
		t[i<<1|1].mi = t[i].tag;
		t[i<<1].sum = 1ll * t[i].tag * (mid-l+1);
		t[i<<1|1].sum = 1ll * t[i].tag * (r-mid);
		t[i].tag = 0;
	}
}

void modify(int i, int k, int l, int r, int x, int y){
	if(x <= l && r <= y){
		t[i].tag = k;
		t[i].mi = k;
		t[i].sum = 1ll * k * (r-l+1);
		return;
	}
	pushdown(i, l, r);
	int mid = (l+r) >> 1;
	if(x <= mid) modify(i<<1, k, l, mid, x, y);
	if(y > mid) modify(i<<1|1, k, mid + 1, r, x, y);
	pushup(i); 
}

int find(int i, int k, int l, int r, int x){
	if(l == r){
		return l;
	}
	
	pushdown(i, l, r);
	
	int mid = (l+r) >> 1;
	int pos = -1;
	if(t[i<<1|1].mi < k) pos = find(i<<1|1, k, mid+1, r, x);
	if(pos != -1) return pos;
	if(x <= mid && t[i<<1].mi < k) pos = find(i<<1, k, l, mid, x);
	return pos;
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	T = read();
	while(T--){
		memset(t, 0, sizeof(t));
		memset(ans, 0, sizeof(ans));
		n = read();
		for(int i = 1 ; i <= n ; ++i) {
			a[i] = read(), p[a[i]] = i; 
		}
		for(int i = 1 ; i <= n ; ++i){
			fac[i].clear();
			for(int j = i ; j <= n ; j += i){
				fac[i].push_back(p[j]);
			}
			sort(fac[i].begin(), fac[i].end());
		}		
		
		build(1, 1, n);
		
		for(int g = n ; g >= 1 ; --g){
			ll sum = t[1].sum;
			for(int i = 0 ; i < fac[g].size()-1 ; ++i){
				int pos = find(1, fac[g][i]+1, 1, n, fac[g][i+1]);
				if(pos != -1 && pos >= fac[g][i+1]) modify(1, fac[g][i]+1, 1, n, fac[g][i+1], pos);
			}
			ans[g] = t[1].sum - sum;
		}
		
		for(int g = 1 ; g <= n ; ++g){
			printf("%lld
", ans[g]);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/15203849.html