2021牛客多校第二场 A——Arithmetic Progression

题目大意

给你一个长度为 n n n 的数列 a a a ,数列中每个元素都不一样,问你存在多少个区间,这些区间内的数排序后是一个等差数列

解题思路

对于一个区间,如果这个区间内的数排序后的元素可以构成一个等差序列,那么一定满足下列性质
m a x − m i n = ( r − l ) × g c d max - min = (r - l) \times gcd maxmin=(rl)×gcd
其中 g c d gcd gcd 是区间差分绝对值的最大公约数
m i n min min 是区间的最小值
m a x max max 是区间的最大值
我们做这题的思想是,枚举右端点,用数据结构维护有多少个合法的左端点
我们用一个线段树来维护这个东西。线段树一个节点维护四个值
d a t : m a x − m i n − ( r − l ) × g c d dat: max-min-(r-l)\times gcd dat:maxmin(rl)×gcd的最小值,右端点为我们枚举的右端点(因为这个式子的最小值为0,当这个值为0时,说明这是一个合法的区间)
c n t : cnt: cnt:合法左端点的个数
a d d : add: add:懒标记
g : g: g:当前节点所代表区间的差分的绝对值的最大公约数
这样我们每次询问区间 [ 1 , r ] [1, r] [1,r] 就可以知道有多少个合法的区间

接下来我们考虑右端点往右移(即新加一个数)的情况。
新加入一个数,可能对之前的 m i n min min m a x max max 有影响,为了解决这个问题,我们用单调栈解决。

我们对于 m i n min min 维护一个上升的单调栈,栈里面存下标
如果新加入的元素比栈顶大,那么直接入栈,它不会对之前区间的 m i n min min 产生影响。
如果新加入的元素小于等于栈顶元素,那么我们将区间 [ [ [ 栈顶第二个元素+1, 栈顶元素 ] ] ] 都加上栈顶元素下标所在的值(这一步是为了消除栈顶元素的影响)
弹出栈顶元素,直到当前元素比栈顶元素大,否则一直执行上述步骤。
然后将区间 [ [ [栈顶元素+1, 当前枚举的右端点 ] ] ] 减去当前元素(这一步是为了加上这新元素的影响)
对于 m a x max max 维护一个下降的单调栈,与上述操作类似,不再赘述。

我们维护完了最大最小值,我们再维护 ( r − l ) × g c d (r-l)\times gcd (rl)×gcd。我们记 d = ∣ a i − 1 − a i ∣ d = |a_{i-1} - a_i| d=ai1ai,我们更新区间 [ 1 , r − 1 ] [1, r-1] [1,r1]

当区间的 g g g 满足 g ! = 0 & &   d ∣ g g != 0 \&\& \ d|g g!=0&& dg时,说明这个区间新加入这个数是能维持 g g g 不变的,所以我们新加入一个数的影响是将这个区间的值减去一个 g g g (因为区间长度变长了,所以要减去一个 g g g 如果不懂再看看 d a t dat dat 的定义)
如果不满足上述条件,我们需要将之间的 ( r − l ) ∗ g (r-l) * g (rl)g加上(消去之前的影响),再将 g = g c d ( g , d ) g = gcd(g, d) g=gcd(g,d),然后减去 ( r − l ) ∗ g (r-l) * g (rl)g,(加上这个数的影响)

Code

#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int n;
int stmi[MAXN], stma[MAXN];
ll a[MAXN];
ll gcd(ll a, ll b){
	return b ? gcd(b, a%b) : a;
}
struct Tree{
	int l, r;
	ll dat, g, add, cnt;
}tree[MAXN << 2];
void pushup(int rt){
	tree[rt].cnt = 0;
	tree[rt].dat = min(tree[rt << 1].dat, tree[rt << 1 | 1].dat);
	if(tree[rt].dat == tree[rt << 1].dat)
		tree[rt].cnt += tree[rt << 1].cnt;
	if(tree[rt].dat == tree[rt << 1 | 1].dat)
		tree[rt].cnt += tree[rt << 1 | 1].cnt;
	if(tree[rt << 1].g && tree[rt << 1 | 1].g && tree[rt << 1].g == tree[rt << 1 | 1].g)
		tree[rt].g = tree[rt << 1].g;
	else
		tree[rt].g = 0;
}
void build(int l, int r, int rt){
	tree[rt].l = l;
	tree[rt].r = r;
	tree[rt].cnt = tree[rt].add = tree[rt].g = tree[rt].dat = 0ll;
	if(l == r){
		tree[rt].cnt = 1ll;
		return ;
	}
	int m = (l + r) >> 1;
	build(l, m, rt << 1);
	build(m+1, r, rt << 1 | 1);
	pushup(rt);
}
void pushdown(int rt){
	if(tree[rt].add){
		tree[rt << 1].dat += tree[rt].add;
		tree[rt << 1].add += tree[rt].add;
		tree[rt << 1 | 1].dat += tree[rt].add;
		tree[rt << 1 | 1].add += tree[rt].add;
		tree[rt].add = 0;
	}
}
void update(int L, int R, ll val, int rt){
	if(L <= tree[rt].l && tree[rt].r <= R){
		tree[rt].dat += val ;
		tree[rt].add += val;
		return;
	}
	pushdown(rt);
	int m = (tree[rt].l + tree[rt].r) >> 1;
	if(L <= m)
		update(L, R, val, rt << 1);
	if(m < R)
		update(L, R, val, rt << 1 | 1);
	pushup(rt);
}
void updateGcd(int R, int l, int r, ll g, int rt){
	if(l <= tree[rt].l && tree[rt].r <= r && tree[rt].g && g % tree[rt].g == 0){
			tree[rt].dat -= tree[rt].g;
			tree[rt].add -= tree[rt].g;
	}
	else if (tree[rt].l == tree[rt].r){
			tree[rt].dat += (R - tree[rt].l - 1) * tree[rt].g;
			tree[rt].add += (R - tree[rt].l - 1) * tree[rt].g;
			tree[rt].g = gcd(g, tree[rt].g);
			tree[rt].dat -= (R - tree[rt].l) * tree[rt].g;
			tree[rt].add -= (R - tree[rt].l) * tree[rt].g;
	}
	else{
		pushdown(rt);
		int m = (tree[rt].l + tree[rt].r) >> 1;
		if(l <= m)
			updateGcd(R, l, r, g, rt << 1);
		if(m < r)
			updateGcd(R, l, r, g, rt << 1 | 1);
		pushup(rt);
	}
	
}
ll query(int L, int R, int rt){
	if(L <= tree[rt].l && tree[rt].r <= R){
		return tree[rt].dat == 0 ? tree[rt].cnt : 0;
	}
	pushdown(rt);
	int m = (tree[rt].l + tree[rt].r) >> 1;
	ll ret = 0;
	if(L <= m)
		ret += query(L, R, rt << 1);
	if(m < R)
		ret += query(L, R, rt << 1 | 1);
	return ret;
}
void solve(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    	scanf("%lld", a+i);
    ll ans = 0;
    build(1, n, 1);
    int topmi = 0, topma = 0;
    for(int i = 1; i <= n; i++){
    	while(topmi && a[stmi[topmi]] > a[i]){
    		update(stmi[topmi - 1] + 1, stmi[topmi], a[stmi[topmi]], 1);
    		topmi--;
    	}
    	update(stmi[topmi] + 1, i, -a[i], 1);
    	stmi[++topmi] = i;
    	while(topma && a[stma[topma]] < a[i]){
    		update(stma[topma - 1] + 1, stma[topma], -a[stma[topma]], 1);
    		topma--;
    	}
    	update(stma[topma] + 1, i, a[i], 1);
    	stma[++topma] = i;
    	if(i != 1){
    		updateGcd(i, 1, i-1, abs(a[i] - a[i-1]), 1);
    	}
    	ans += query(1, i, 1);
    }
    printf("%lld\n",ans);
}

int main()
{
	#ifdef ONLINE_JUDGE
    #else
       freopen("in.txt", "r", stdin);
       freopen("out.txt", "w", stdout);
    #endif

	// qc;
    int T;
    // cin >> T;
    scanf("%d",&T);
    // T = 1;
    while(T--){

        solve();
    }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值