C. Sasha and Array(线段树+矩阵快速幂)

题意:给出一个序列a,给出两种操作,操作1:对a数组中 [ l , r ] [l,r] [l,r]范围的内的数加上一个数x,操作2:查询 [ l , r ] [l,r] [l,r]范围内的 f [ a [ i ] ] f[a[i]] f[a[i]],其中 f [ x ] f[x] f[x],是指第x个斐波那契数。
思路:显然对于第一种操作,这里需要用到区间修改,所以要用到懒标记的线段树,对于第二种操作,可以很快地想到用矩阵加速递推式。于是开一个结点信息是矩阵的线段树,维护每个点的斐波那契和,懒标记写成转移矩阵的幂次,就可以非常清晰做出来这题。

const int N = 2, M = 1e5 + 50;
int w[M];	
struct mat
{
	ll A[N][N];
	mat() { memset(A, 0, sizeof A); }
	mat operator * (const mat &b) const
	{
		mat tmp;
		f(i,0,N-1)
		f(j,0,N-1)
			f(k,0,N-1)
				tmp.A[i][k] = (tmp.A[i][k] + 1ll * A[i][j] * b.A[j][k]) %mod;
		return tmp;
	}
}tr[M << 2],lz[M<<2],f,A1;
mat jzksm(mat a, int n)
{
	mat tmp;
	tmp.A[0][0]=tmp.A[1][1]=1;
	while (n)
	{
		if (n & 1) tmp= tmp * a;
		a=a*a;
		n >>= 1;
	}
	return tmp;
}
void pushup(int u)
{
	tr[u].A[0][0] = (tr[u << 1].A[0][0] + tr[u << 1 | 1].A[0][0]) % mod;
	tr[u].A[0][1] = (tr[u << 1].A[0][1] + tr[u << 1 | 1].A[0][1]) % mod;
}
void pushdown(int u)
{
	tr[u<<1]= tr[u<<1]*lz[u];
	lz[u << 1] = lz[u << 1] * lz[u];
	tr[u << 1 | 1] = tr[u << 1 | 1] * lz[u];
	lz[u << 1 | 1] = lz[u << 1 | 1] * lz[u];
	lz[u].A[0][0] = 1, lz[u].A[1][1] = 1, lz[u].A[0][1] = 0, lz[u].A[1][0] = 0;
}
void build(int i, int l, int r)
{
	if (l == r)
	{
		tr[i].A[0][0] = 1;tr[i].A[0][1] = 1;tr[i].A[1][0] = 1;
		tr[i]=jzksm(tr[i], w[l]-1);	
		tr[i] = A1 * tr[i];
		lz[i].A[0][0] = lz[i].A[1][1] = 1;
		return;
	}
	lz[i].A[0][0] = lz[i].A[1][1] = 1;
	int mid = (l + r) >> 1;
	build(i << 1, l, mid),build(i << 1 | 1, mid + 1, r);
	pushup(i);
}
void modify(int u, int l, int r, int L, int R, mat w)
{
	if (l >= L && r <= R)
	{
		tr[u] = tr[u] * w;
		lz[u] = lz[u] * w;
		return;
	}
	else
	{
		pushdown(u);
		int mid = l + r >> 1;
		if (L <= mid)modify(u << 1, l, mid, L, R, w);
		if (R > mid)modify(u << 1 | 1, mid + 1, r, L, R, w);
		pushup(u);
	}
}
ll query(int u, int l, int r,int L,int R)//当前查询范围,目标查询范围
{
	if (l >= L && r <= R)return tr[u].A[0][0];
	pushdown(u);
	int mid = l+r>> 1;
	ll sum = 0;
	if (L <= mid)sum =sum + query(u << 1,l,mid,L,R);
	if (R > mid)sum += query(u << 1 | 1,mid+1,r,L,R);
	return sum %mod;
}
int main()
{
#ifndef ONLINE_JUDGE 
	freopen("in.txt", "r", stdin);
#endif
	f.A[0][0] = 1;
	f.A[0][1] = 1;
	f.A[1][0] = 1;
	A1.A[0][0] = 1;
	int n, q;
	cin >> n >> q;
	f(i, 1, n)w[i] = in();
	build(1, 1, n);
	while (q--)
	{
		int op = in();
		if (op == 1)
		{
			int l = in(), r = in(), x = in();
			f=jzksm(f, x);
			modify(1, 1, n, l, r, f);
			f.A[0][0] = f.A[0][1] = f.A[1][0] = 1;f.A[1][1] = 0;
		}
		else
		{
			int l = in(), r = in();
			printf("%lld\n", query(1, 1, n, l, r));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值