【LOJ NOI Round#2 Day1 T1】单枪匹马【矩阵】

传送门

支持后面加数,区间进行奇怪的要求操作算值。

你看这个操作是个线性的迭代,这种时候就应该想到线性代数,那就想到矩阵。

你就强行用矩阵表示,然后手玩一下结果矩阵来反推原来的矩阵。

手玩出来每个数的初始矩阵是

0 1

1 a[i]

其中右下角是分子,右上角是分母。

正确性?找两个矩阵乘起来就正确了。

那么一个区间可以视作1到r的矩阵乘去掉1到l-1。

这个去掉操作联想乘逆元,所以乘矩阵逆元前缀积就行。

一个矩阵的逆元无非就是两者乘起来为单位矩阵。

那一个初始矩阵的逆元就是

-a[i] 1

1 0

为什么?还是手玩啊。

然后我们来看这个gcd的问题。gcd和取模是不能共生的。

但是!

这道题很nb的在于它欺骗了你,因为任何时候分子分母都是互质的。

从最开始两个整数一个倒过来加上去再加1肯定是互质的。就算你是1也可以把分母去掉。

其次,假设一个最简分数加上一个整数,那肯定还是互质的啊。要不然早就约了。

所以大胆地取模就是了。

小细节:注意,矩阵不满足交换律,所以逆元矩阵的前缀积处理要和原来矩阵前缀积反着来。这样两者中间相遇的才是对应的矩阵。

// 好自闭啊这个题,,草稿打的头都秃了。
// 甚至暴力只能从右往左迭代,还不能往右扩展,莫队都没法。
// 想到最后只能求助于矩阵。
// 这是一个固定的变换格式,只要我能用矩阵表示出来我就赢了
// 发现卧槽还真能表示出来。
// 然后被gcd和取模的问题卡了很久很久
// 最后发现不对啊,一个最简分数加上一个整数好像还是最简分数吧
// 那我为啥不直接膜呢。
// 就完了,,那个gcd真tm唬人
// 小细节:卡了我半小时,,因为矩阵没有交换律,,手玩了几个发现前缀积的处理还要注意顺序,, 

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
struct node{
	int a[3][3];
};
const int mod=998244353;
node operator *(node a,node b){
	node ans;memset(ans.a,0,sizeof(ans.a));
	for(int i=1;i<=2;i++){
		for(int j=1;j<=2;j++){
			for(int k=1;k<=2;k++){
				ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;;
			}
		}
	}return ans;
}int n,m,type,lastx,lasty;
node fac[1000003],ifac[1000003];
node prefac[1000003],preifac[1000003];
int a[1000003];
void insert(int i,int x){
	fac[i].a[1][2]=fac[i].a[2][1]=1;fac[i].a[2][2]=x;
	ifac[i].a[1][1]=mod-x;ifac[i].a[2][1]=ifac[i].a[1][2]=1;
	prefac[i]=prefac[i-1]*fac[i];
	preifac[i]=ifac[i]*preifac[i-1];
}
node fzone,fmone;
int Gcd(int x,int y){
	if(!y)return x;
	return Gcd(y,x%y);
}
signed main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	n=in;m=in;type=in;prefac[0].a[1][1]=prefac[0].a[2][2]=preifac[0].a[1][1]=preifac[0].a[2][2]=1;
	for(int i=1;i<=n;i++){
		a[i]=in;insert(i,a[i]);
	}int op,x,y;
	while(m--){
		op=in;x=in;if(type)x=x^lastx^lasty;
		if(op==1){
			a[++n]=x;insert(n,a[n]);
		}else{
			y=in;if(type)y=y^lastx^lasty;
			//cout<<x<<" "<<y<<"@# "<<endl;
			//cout<<prefac[y].a[2][2]<<" "<<prefac[y].a[2][1]<<"@#  3#"<<endl;;
			node ans=preifac[x-1]*prefac[y];
			int fm=ans.a[1][2],fz=ans.a[2][2];
//			int gcd=Gcd(fz,fm);
//			lastx=fz/gcd;lasty=fm/gcd;
			lastx=fz,lasty=fm;
			printf("%lld %lld\n",lastx,lasty);
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值