2021-05-20

CF228D Zigzag 树状数组解法

我们先来考虑若没有特殊数列的性质 , 将其直接转化成一个普通数列 S S S ,此时我们的暴力做法是 O ( N 2 ) O(N^2) O(N2)的 , 若使用二维树状数组 , 对于 ∀ i ∈ [ 1 , n ] \forall i \in [1 , n] i[1,n] 都开一个 [ i , n ] [i , n] [i,n]的树状数组的时间复杂度是 O ( l o g n N 2 ) O(log_n N^2) O(lognN2)的。显然对于每一做法我们都是不能接受的 , 所以我们必须对这个特殊的数列下手 , 观察一下发现 , 这是一个以 2 ( z − 1 ) 2(z - 1) 2(z1)为周期的数列 , 而且 Z Z Z非常的小,给了我们很多优化的空间 , 考虑对解法二进行优化 , 这时因为数列的周期性 , 对于第一个数出现的所有余数开成第二维即可。

对于操作1:暴力修改即可
操作2:对于第一位为Z , 第二维为此时(QL的余数为1时)第一个数的余数 , 再区间求和即可。

考虑到我的描述非常烂 ,便扔一个代码。

#include <bits/stdc++.h>
// WIN
// 加油子
using namespace std ;
typedef long long LL ;
typedef unsigned u64 ;
typedef unsigned uint ;
typedef pair<int , int> PII ;

#define rep(i , a , b) for (int i = (a) ; i <= (b) ; ++ i)
#define per(i , a , b) for (int i = (a) ; i >= (b) ; -- i)

const int P = 1e9 + 7 , mo1 = 1234567891 , mo2 = 1e9 + 123 ;
const int N = 1e3 + 12 , Base = 131 , M = 2e3 + 13 ;


template <class Tv> 
Tv umin (Tv a , Tv b) {return a < b ? a : b ;}

template <class Tv>
Tv umax (Tv a , Tv b) {return a > b ? a : b ;}



int n , m , s[7][90] ;
int a[100005] ; 

struct BIT {
	LL C[100005] ;
	LL& operator [] (int alp) {return C[alp] ;}
	int lowbit (int x) {return x & (-x) ;}
	void modify (int x , LL k) {
		for (int i = x ; i <= n ; i += lowbit(i)) C[i] += k ;
	}
	void pri () {
		for (int i = 1 ; i <= n ; i ++) {
			cout << i << ':' << C[i] << endl ;
		}
	}
	LL query (int x) {
		LL res = 0 ;
		for (int i = x ; i ; i -= lowbit(i)) {
			res += C[i] ;
		}
		return res ;
	}
}bit[7][13] ;

int omi (int v , int z) {
	int alp = v % (2 * z - 2) ;
	if (!alp) return s[z][v] = 2 , 2 ;
	else if (alp > z) return s[z][v] = 2 * z - alp , s[z][v] ;
	else return s[z][v] = alp , alp ;
}

int pos (int i , int a , int b) {
	if (i == 1) return a ;
	int tmp = i % b ;
	tmp = !tmp ? b : tmp ;
	if (a >= tmp) return a - tmp + 1 ;
	else return b + 1 - (tmp - a) ; 
}
void change (int ip , int val) {
	for (int j = 2 ; j <= 6 ; j ++) 
		for (int k = 1 ; k <= 2 * (j - 1) ; k ++) 
			bit[j][pos(ip , k , 2 * j - 2)].modify (ip , 1ll * (val - a[ip]) * s[j][k]) ; 
	a[ip] = val ;
}


int main () {
	scanf ("%d" , &n) ;
	for (int j = 2 ; j <= 6 ; j ++) {
		for (int k = 1 ; k <= 2 * (j - 1) ; k ++) omi (k , j) ;
	}
	for (int i = 1 ; i <= n ; i ++) {
		scanf ("%d" , & a[i]) ;
		for (int j = 2 ; j <= 6 ; j ++) {
			for (int k = 1 ; k <= 2 * (j - 1) ; k ++) {
				bit[j][pos(i , k , 2 * j - 2)].modify (i , 1ll * a[i] * s[j][k]) ; 				
			}
		}
	}
	scanf ("%d" , &m) ;
	while (m--) { 
		int opt ; 
		scanf ("%d" , &opt) ; 
		if (opt == 1) {
			int ip , v ; 
			scanf ("%d%d" , &ip , &v) ; 
			change (ip , v) ;
		}
		else {
			int ql , qr , z ;
			scanf ("%d%d%d" , &ql , &qr , &z) ; 
			int ip = pos (ql , 1 , (z - 1) * 2) ;
			LL res = bit[z][ip].query (qr) - bit[z][ip].query(ql - 1) ;
			printf ("%lld\n" , res) ;
		}
	}
	return 0 ;
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值