LOJ #3536. 「NOI2021」密码箱

题目叙述

维护序列 \(a_0\sim a_n\) 。有两种操作:

  • W 表示最后一个数 +1
  • E 表示如果最后一个数是 1,那么倒数第二个 +1,否则给最后一个数 -1,后面再追加两个 1。
    最初整个序列只有两个数,\(a_0=0\)\(a_1=1\)
    现在维护 W 和 E 的序列。
    支持区间翻转,区间W变E,E变W,和在结尾追加一个字符三种操作。
    每次操作完了之后,询问下面算式计算出来分子分母约分后是多少:

\[a_0+\dfrac{1}{a_1+\dfrac{1}{a_2+\dfrac{1}{a_3+\cdots}}} \]

题解

可能,遇到看起来需要大分类讨论的数据结构题,把维护的东西改成一个矩阵不失为一种很好的策略。
先考虑连分数怎么用矩阵的形式计算。

\[\prod_{i=1}^n \begin{bmatrix} a_i& 1\\ 1& 0 \end{bmatrix} \begin{bmatrix} 1\\ 0 \end{bmatrix} \]

这个东西最后乘除来,上面的是分子,下面的是分母。
然后考虑操作带来的影响是什么。比如最后一个数 +1 ,带来的影响是乘上一个 \(\begin{bmatrix}1& 0\\1& 1\end{bmatrix}\)
那个给最有一个减一再添加两个 1 呢,其实相当于乘上 \(\begin{bmatrix}2& 1\\-1& 0\end{bmatrix}\)
然后平衡树维护 取反/不取反,翻转/不翻转。维护即可。
顺便一说,这题犯了一个错调了我很久。原因是 pushup 的时候要写四个相同的东西,最后一个的最后一部分写错了,左儿子写成右儿子了。

总结

  • 形势复杂的东西考虑矩阵维护。
  • 调试的时候认为可能错的地方要给予他一些机会,但是不能只调那里,同时也要调试那些比较确定的地方,避免“灯下黑”。
  • 下次预测一下哪里容易写错。这很重要。

代码

有人说FHQ-Treap跑不过splay,我说他在扯淡。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <random>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const int MN=1e5+5,Mod=998244353;
int ad(int x,int y){return ((x+y)>=Mod)?(x+y-Mod):(x+y);}
int dc(int x,int y){return ((x-y)<0)?(x-y+Mod):(x-y);}
int ml(int x,int y){return (LL)x*y%Mod;}
int N,Q;
struct Mat{
	int A[2][2];
	Mat(){memset(A,0,sizeof(A));}
	Mat(int a,int b,int c,int d){A[0][0]=a,A[0][1]=b,A[1][0]=c,A[1][1]=d;}
	int* operator[](const int &x){return A[x];}
	const int* operator[](const int &x)const {return A[x];}
};
Mat operator*(const Mat &x,const Mat &y){
	Mat ret;
	ret[0][0]=((LL)x[0][0]*y[0][0]+(LL)x[0][1]*y[1][0])%Mod;
	ret[0][1]=((LL)x[0][0]*y[0][1]+(LL)x[0][1]*y[1][1])%Mod;
	ret[1][0]=((LL)x[1][0]*y[0][0]+(LL)x[1][1]*y[1][0])%Mod;
	ret[1][1]=((LL)x[1][0]*y[0][1]+(LL)x[1][1]*y[1][1])%Mod;
	return ret;
}
const Mat opr[]={Mat(1,0,1,1),Mat(2,1,Mod-1,0)};
const int MND=MN*2;
int rnd[MND],ls[MND],rs[MND],siz[MND],totnode;
Mat sum[MND][2][2];
bool type[MND],tf[MND],tr[MND];
mt19937 myrand(1145);
// 0/1 flip,0/1 reverse
int new_node(bool t){
	++totnode;
	sum[totnode][0][0]=opr[t];
	sum[totnode][0][1]=opr[t];
	sum[totnode][1][0]=opr[t^1];
	sum[totnode][1][1]=opr[t^1];
	type[totnode]=t;
	rnd[totnode]=myrand();
	siz[totnode]=1;
	return totnode;
}
void upd(int o){
	if(!o)return;
	int l=ls[o],r=rs[o];
	sum[o][0][0]=sum[l][0][0]*opr[type[o]]*sum[r][0][0];
	sum[o][0][1]=sum[r][0][1]*opr[type[o]]*sum[l][0][1];
	sum[o][1][0]=sum[l][1][0]*opr[type[o]^1]*sum[r][1][0];
	sum[o][1][1]=sum[r][1][1]*opr[type[o]^1]*sum[l][1][1];
	siz[o]=siz[l]+1+siz[r];
}
void flip(int o){type[o]^=1,swap(sum[o][0][0],sum[o][1][0]),swap(sum[o][0][1],sum[o][1][1]),tf[o]^=1;}
void reverse(int o){swap(ls[o],rs[o]),swap(sum[o][0][0],sum[o][0][1]),swap(sum[o][1][0],sum[o][1][1]),tr[o]^=1;}
void spread(int o){
	if(tf[o]){
		if(ls[o])flip(ls[o]);
		if(rs[o])flip(rs[o]);
		tf[o]=0;
	}
	if(tr[o]){
		if(ls[o])reverse(ls[o]);
		if(rs[o])reverse(rs[o]);
		tr[o]=0;
	}
}
int merge(int x,int y){
	if((!x)||(!y))return x|y;
	if(rnd[x]<rnd[y]){
		spread(y);
		ls[y]=merge(x,ls[y]);
		return upd(y),y;
	}else{
		spread(x);
		rs[x]=merge(rs[x],y);
		return upd(x),x;
	}
}
void split(int o,int &x,int &y,int s){
	if(!o)return x=y=0,void();
	spread(o);
	if(s<=siz[ls[o]]){
		y=o;
		split(ls[o],x,ls[y],s);
		upd(y);
	}else{
		x=o;
		split(rs[o],rs[x],y,s-siz[ls[o]]-1);
		upd(x);
	}
}
int rt;
void append(bool type){rt=merge(rt,new_node(type));}
void flip(int l,int r){
	int x=0,y=0,z=0;
	split(rt,x,y,l-1);
	split(y,y,z,r-l+1);
	flip(y);
	y=merge(y,z);
	rt=merge(x,y);
}
// void reverse(int l,int r){
	
// }
char str[MN];
const Mat a(1,0,1,1); // 别忘了乘上这个矩阵
int main(){
	freopen("code.in","r",stdin);
	freopen("code.out","w",stdout);
	scanf("%d%d",&N,&Q);
	sum[0][0][0]=sum[0][0][1]=sum[0][1][0]=sum[0][1][1]=Mat(1,0,0,1);
	scanf("%s",str+1);
	FOR(i,1,N)append(str[i]=='E');
	Mat tmp=a*sum[rt][0][0];
	printf("%d %d\n",tmp[0][0],tmp[1][0]);
	while(Q--){
		static char str[10];
		scanf("%s",str+1);
		if(str[1]=='A'){
			static char s2[10];
			scanf("%s",s2+1);
			append(s2[1]=='E');
		}else if(str[1]=='F'){
			static int l=0,r=0;
			scanf("%d%d",&l,&r);
			flip(l,r);
		}else{
			static int l=0,r=0;
			scanf("%d%d",&l,&r);
			int x=0,y=0,z=0;
			split(rt,x,y,l-1);
			split(y,y,z,r-l+1);
			reverse(y);
			y=merge(y,z);
			rt=merge(x,y);
		}
		Mat tmp=a*sum[rt][0][0];
		printf("%d %d\n",tmp[0][0],tmp[1][0]);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2022-07-22 16:11  YouthRhythm  阅读(68)  评论(0)    收藏  举报