K. Addition Robot 2019-2020 ICPC, Asia Jakarta Regional Contest 线段树

LINK

K. Addition Robot
time limit per test3 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Adding two numbers several times is a time-consuming task, so you want to build a robot. The robot should have a string S=S1S2…SN of N characters on its memory that represents addition instructions. Each character of the string, Si, is either ‘A’ or ‘B’.

You want to be able to give Q commands to the robot, each command is either of the following types:

1 L R. The robot should toggle all the characters of Si where L≤i≤R. Toggling a character means changing it to ‘A’ if it was previously ‘B’, or changing it to ‘B’ if it was previously ‘A’.
2 L R A B. The robot should call f(L,R,A,B) and return two integers as defined in the following pseudocode:
function f(L, R, A, B):
FOR i from L to R
if S[i] = ‘A’
A = A + B
else
B = A + B
return (A, B)

You want to implement the robot’s expected behavior.

Input
Input begins with a line containing two integers: N Q (1≤N,Q≤100000) representing the number of characters in the robot’s memory and the number of commands, respectively. The next line contains a string S containing N characters (each either ‘A’ or ‘B’) representing the initial string in the robot’s memory. The next Q lines each contains a command of the following types.

1 L R (1≤L≤R≤N)
2 L R A B (1≤L≤R≤N; 0≤A,B≤109)
There is at least one command of the second type.
Output
For each command of the second type in the same order as input, output in a line two integers (separated by a single space), the value of A and B returned by f(L,R,A,B), respectively. As this output can be large, you need to modulo the output by 1000000007.
Example
inputCopy
5 3
ABAAA
2 1 5 1 1
1 3 5
2 2 5 0 1000000000
outputCopy
11 3
0 1000000000

在这里插入图片描述CF 的时候完全没思路QAQ
第一份code是看了大佬的代码(%%%)照着思路打的
照着模拟一遍 get 到了一些点 下下面还有自己的一点idea(叙述过于冗长)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,mod=1e9+7;
typedef long long ll;
int n,m;char s[N];
struct node{
	ll m[2][2];
	node(){
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) m[i][j]=0;
	}
	friend node operator*(const node &a,const node &b)
	{//矩阵相乘 
		node c;
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++)
		{
		c.m[i][j]+=a.m[i][k]*b.m[k][j]; c.m[i][j]%=mod;	
		}
		return c;
	}
	void change(){//矩阵转置 
		swap(m[1][1],m[0][0]);swap(m[1][0],m[0][1]);
	}
}t[N<<2],A,B;
int tag[N<<2];//懒标记 
void build(int l,int r,int rt)
{
	if(l==r)
	{
		if(s[l]=='A') t[rt]=A;
		else t[rt]=B; 
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	t[rt]=t[rt<<1]*t[rt<<1|1];
}
void pushdown(int rt)
{
	tag[rt]=0;
	tag[rt<<1|1]^=1;
	tag[rt<<1]^=1;
	t[rt<<1].change();
	t[rt<<1|1].change(); 
}
void update(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R) 
	{
		tag[rt]^=1;
		t[rt].change();
		return ;
	}
	if(tag[rt]) pushdown(rt);
	int mid=(l+r)>>1;
	if(L<=mid) update(L,R,l,mid,rt<<1);
	if(R>mid) update(L,R,mid+1,r,rt<<1|1);
	t[rt]=t[rt<<1]*t[rt<<1|1];
}
node query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R) 
	{
		return t[rt];
	}
	if(tag[rt]) pushdown(rt);
	int mid=(l+r)>>1;
	node ans;
	ans.m[0][0]=ans.m[1][1]=1;
	if(L<=mid) ans=(ans*query(L,R,l,mid,rt<<1));
	if(R>mid)  ans=(ans*query(L,R,mid+1,r,rt<<1|1));
	return ans;
}
int main()
{
	A.m[0][1]=A.m[0][0]=A.m[1][1]=1;//A=A+B
	B.m[1][1]=B.m[1][0]=B.m[0][0]=1; //B=A+B 
	cin>>n>>m>>(s+1);
	reverse(s+1,s+1+n);
	build(1,n,1);int l, r;ll a,b;
	while(m--)
	{
		int op;scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d",&l,&r);
			update(n+1-r,n+1-l,1,n,1);//更新 
		}else 
		{
			scanf("%d%d%lld%lld",&l,&r,&a,&b);
			node ans=query(n+1-r,n+1-l,1,n,1);//询问 
			printf("%lld %lld\n",(ans.m[0][1]*b%mod+ans.m[0][0]*a%mod)%mod,(ans.m[1][0]*a%mod+ans.m[1][1]*b%mod)%mod);
		}
	}
}

->->仔细想想自己改了一下就是正常的矩阵相乘A * B=C 就应该是C中(X,Y)位置上的数值就应该是A中 X这一行的数乘以对应的B 中Y列上面的数之和 稍微改变一下,考虑矩阵C(X,Y)上的数是由 A X列的数乘以B Y行的数之和

二维矩阵(矩阵用来存系数啊
X Y----A
Z K----B
(X ,Z分别表示 此时 A B中a的系数,同理Y ,K 分别表示A B中b的系数
AA BB也就是代码里的A,B这里为了不弄混
AA矩阵://对应A =A+B
1 1
0 1

BB矩阵://B=A+B
1 0
1 1
考虑~对应的元素是A就把目前B累计的a,b,系数全部累加到A上,B不变
对应的是B就把A的a,b系数全部累加到B上 A不变
按以上的想法):对于A B序列从A到B 采用ans=pre(之前的矩阵也就是AA )*目前的元素矩阵(BB) =
AA *BB =
1 1
1 2

对于所得的矩阵也就是即为C C(0,0)=AA(0,0)*BB(0,0)+AA(1,0)*BB(0,1)
=1 * 1+0 * 1=1 也就是ans中A对应的a的系数 …
这时候就是上面矩阵AA BB 的作用 BB的第一行1 0 与前面pre中的a系数分别相乘,现在对于此时A的a系数只能接受来自上一个A本身的a而拒绝来自上一个B的a ,而第二行 1 1 表示此时的B的a(或b)可以接受来自之前A 的a(b)也可以接受来自之前B的a(b)
其余同理啊

~~感觉越讲越乱啊 明明超级简单的QAQ


代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,mod=1e9+7;
typedef long long ll;
int n,m;char s[N];
struct node{
	ll m[2][2];
	node(){
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) m[i][j]=0;
	}
	//和上一份code主要区别就是下面的opertor里面的a b交换位子,实现矩阵乘法的重置!!!
	friend node operator*(const node &b,const node &a)
	{//矩阵相乘 
		node c;
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++)
		{
		c.m[i][j]+=a.m[i][k]*b.m[k][j]; c.m[i][j]%=mod;	
		}
		return c;
	}
	void change(){//矩阵转置 
		swap(m[1][1],m[0][0]);swap(m[1][0],m[0][1]);
	}
}t[N<<2],A,B;
int tag[N<<2];//懒标记 
void build(int l,int r,int rt)
{
	if(l==r)
	{
		if(s[l]=='A') t[rt]=A;
		else t[rt]=B; 
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	t[rt]=t[rt<<1]*t[rt<<1|1];
}
void pushdown(int rt)
{
	tag[rt]=0;
	tag[rt<<1|1]^=1;
	tag[rt<<1]^=1;
	t[rt<<1].change();
	t[rt<<1|1].change(); 
}
void update(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R) 
	{
		tag[rt]^=1;
		t[rt].change();
		return ;
	}
	if(tag[rt]) pushdown(rt);
	int mid=(l+r)>>1;
	if(L<=mid) update(L,R,l,mid,rt<<1);
	if(R>mid) update(L,R,mid+1,r,rt<<1|1);
	t[rt]=t[rt<<1]*t[rt<<1|1];
}
node query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R) 
	{
		return t[rt];
	}
	if(tag[rt]) pushdown(rt);
	int mid=(l+r)>>1;
	node ans;
	ans.m[0][0]=ans.m[1][1]=1;
	if(L<=mid) ans=(ans*query(L,R,l,mid,rt<<1));
	if(R>mid)  ans=(ans*query(L,R,mid+1,r,rt<<1|1));
	return ans;
}
int main()
{
	A.m[0][1]=A.m[0][0]=A.m[1][1]=1;//A=A+B
	B.m[1][1]=B.m[1][0]=B.m[0][0]=1; //B=A+B 
	cin>>n>>m>>(s+1);
	//reverse(s+1,s+1+n);
	build(1,n,1);int l, r;ll a,b;
	while(m--)
	{
		int op;scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d",&l,&r);
			update(l,r,1,n,1);//更新 
		}else 
		{
			scanf("%d%d%lld%lld",&l,&r,&a,&b);
			node ans=query(l,r,1,n,1);//询问 
			printf("%lld %lld\n",(ans.m[0][1]*b%mod+ans.m[0][0]*a%mod)%mod,(ans.m[1][0]*a%mod+ans.m[1][1]*b%mod)%mod);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值