codeforces 1252K Addition Robot【线段树+矩阵乘法】

题目大意

给你一个串 包含AB两种元素,进行以下两种操作

1、给定一个区间[l,r][l,r][l,r] 将 A 变成 B,B 变成 A

2、给定一个查询区间 [l,r][l,r][l,r] 以及 两个数 x,y

在区间 [l,r][l,r][l,r]中如果 s[i]=As[i] =As[i]=A 那么 x = x+y
否则 y=x+y

查询这个区间最后的x,y分别是多少

题目分析

刚拿到这个题有些无从分析

我们考虑
x=x+y 、 y=x+y
这两个操作,发现可以写成矩阵的形式

在这里插入图片描述
所以,我们考虑线段树维护区间矩阵乘积

那么当我们在进行修改的时候,对于一个区间而言,其实乘积只有可能有两种。所以每个点维护这两种乘积即可。

如果修改区间的话,就交换这两种乘积。然后修改父亲结点即可。

代码详解
#include <bits/stdc++.h>
using namespace std;
const int maxn =4e5+50;
const int mod = 1e9+7;
typedef long long ll;
string s;
struct Mat
{
	int a[2][2];
};
Mat A,B,E;
Mat mul(Mat x,Mat y)
{
	Mat t;
	
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		{
			t.a[i][j]=0;
			for(int k=0;k<2;k++)
				t.a[i][j] += ((ll)x.a[i][k] * (ll)y.a[k][j])%mod;
				t.a[i][j] %=mod;
		}
	 return t;
}
struct Node
{
	int l,r;
	Mat w[2];
	int flag = 0;
}node[maxn];
void pt(int root)
{
	cout<<root<<endl;
	cout<<node[root].l<<" "<<node[root].r<<endl;
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<2;j++)
		{
			cout<<node[root].w[0].a[i][j]<<" ";
		}
		cout<<endl;
	}
}
void pushup(int root)
{
	node[root].w[0] =mul(node[root*2].w[0],node[root*2+1].w[0]);
	node[root].w[1] =mul(node[root*2].w[1],node[root*2+1].w[1]);
}
void pushdown(int root)
{
	if(node[root].flag)
	{
		node[root*2].flag^=1;
		swap(node[root*2].w[0],node[root*2].w[1]);
		node[root*2+1].flag^=1;
		swap(node[root*2+1].w[0],node[root*2+1].w[1]);
		node[root].flag^=1;
	}
}
void build(int root,int l,int r)
{
	if(l>r) return ;
	node[root].l = l;
	node[root].r = r;
	node[root].flag=0;
	if(l==r) 
	{
		if(s[l-1]=='A') 
		node[root].w[0] =A,node[root].w[1] = B;
		else node[root].w[0]=B,node[root].w[1] =A;
		//pt(root);
		return;
	} 
	int mid = (l+r)/2;
	build(root*2,l,mid);
	build(root*2+1,mid+1,r);
	pushup(root);
	//pt(root);
}
void update(int root,int st,int ed)
{

	int l = node[root].l;
	int r = node[root].r;
	//cout<<st<<" "<<ed<<" "<<l<<" "<<r<<endl;
	if(st>r||ed<l) return;
	if(st<=l&&ed>=r)
	{
		node[root].flag^=1;
		swap(node[root].w[0],node[root].w[1]);
		//pt(root);
		return;
	}
	pushdown(root);
	update(root*2,st,ed);
	update(root*2+1,st,ed);
	pushup(root);
	//pt(root);
} 
Mat query(int root,int st,int ed)
{
	int l = node[root].l;
	int r = node[root].r;
	if(l>ed||r<st) return E;
	if(st<=l&&ed>=r)
	{
		return node[root].w[0]; 
	} 
	Mat x=E;
	pushdown(root);
	x=mul(x,query(root*2,st,ed));
	x=mul(x,query(root*2+1,st,ed));
	pushup(root);
	return x;
}
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	cin>>s;
	A.a[0][0] = 1; A.a[0][1] = 0; A.a[1][0] = 1; A.a[1][1] = 1;
	B.a[0][0] = 1; B.a[0][1] = 1; B.a[1][0] = 0; B.a[1][1] = 1;
	E.a[0][0] = 1; E.a[0][1] = 0; E.a[1][0] = 0; E.a[1][1] = 1;
	build(1,1,n);
	for(int i=1;i<=q;i++)
	{
		int op;
		scanf("%d",&op);
		if(op==1) 
		{
			int l,r;
			scanf("%d%d",&l,&r);
			update(1,l,r);
		}
		if(op==2)
		{
			int l,r,x,y;
			scanf("%d%d%d%d",&l,&r,&x,&y);
			Mat t = query(1,l,r);
			ll le =((ll)t.a[0][0]*(ll)x%mod+(ll)t.a[1][0]*(ll)y%mod)%mod;
			ll ri = ((ll)t.a[0][1]*(ll)x%mod+ (ll) t.a[1][1]*(ll)y%mod)%mod;
			printf("%I64d %I64d\n",le,ri); 
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值