• P7739[NOI2021]密码箱【Splay,矩阵乘法】


    正题

    题目链接:https://www.luogu.com.cn/problem/P7739


    题目描述

    懒得概括,摸了。

    Yelekastee 是 U 国著名的考古学家。在最近的一次考古行动中,他发掘出了一个远古时期的密码箱。经过周密而严谨的考证,Yelekastee 得知密码箱的密码和某一个数列 \(\{ a_n \}\) 相关。数列 \(\{ a_n \}\) 可以用如下方式构造出来:

    1. 初始时数列长度为 \(2\) 且有 \(a_0 = 0, a_1 = 1\)
    2. 对数列依次进行若干次操作,其中每次操作是以下两种类型之一:
    • W 类型:给数列的最后一项\(1\)
    • E 类型:若数列的最后一项\(1\),则给倒数第二项加 \(1\);否则先给数列的最后一项\(1\),接着在数列尾再加两项,两项的值都是 \(1\)

    受到技术限制,密码箱并没有办法完整检查整个数列,因此密码箱的密码设定为数列 \(\{ a_n \}\) 经过函数 \(f\) 作用后的值,其中 \(f\) 的定义如下:

    \[f(a_0, \ldots , a_{k - 1}, a_k) = \begin{cases} a_0, & k = 0 \\ f \! \left( a_0, a_1, \ldots , a_{k - 2}, a_{k - 1} + \frac{1}{a_k} \right) \! , & k \ge 1 \end{cases} \]

    Yelekastee 并不擅长运算,因此他找到了你,希望你能根据他提供的操作序列计算出密码箱的密码。不幸的是,他的记性并不是很好,因此他会随时对提供的操作序列做出一些修改,这些修改包括以下三种:

    • APPEND c,在现有操作序列后追加一次 c 类型操作,其中 c 为字符 WE
    • FLIP l r,反转现有操作序列中第 \(l\) 个至第 \(r\) 个(下标从 \(1\) 开始,修改包含端点 \(l\)\(r\),下同)操作,即所有 W 变为 E,所有 E 变为 W
    • REVERSE l r,翻转现有操作序列中第 \(l\) 个至第 \(r\) 个操作,也就是将这个区间中的操作逆序。

    \(1\leq n,q\leq 10^5\)


    解题思路

    先考虑知道\(a\)序列怎么求答案,假设上一个传下来的是\(\frac{x}{y}\),那么新的那个就是\(\frac{x'}{y'}=a_k+\frac{x}{y}=\frac{ya_k+x}{x}\),那么有\(x'=ya_k+x,y'=x\)

    显然如果我们只是动态修改每一个\(a\),那么上面这个转移可以用矩阵乘法维护。

    但是我们现在的操作可能是让一些数\(+1\)和在数列后面\(+1\)。麻烦的是让一些数\(+1\)这个操作,我们考虑如果我们在末尾加入了一个\(1\),那么转移\(x'=x+y,y'=x\),如果让末尾的一个数加\(1\),那么\(x'=x+y,y'=y\)。这样就可以直接考虑矩阵维护\(EW\)操作了,我们分别设上面两个操作的矩阵为\(H_A,H_B\),我们求出一个\(G_A\times H_A=1,G_B\times H_B=1\)这样就可以取消掉前面的一些操作了。

    然后考虑一下下面的操作怎么实现,设目前的矩阵乘积为\(M\)

    1. 让末尾\(+1\)\(M'=H_A\times H_B\times G_A\times M\)
    2. 让倒数第二个\(+1\):注意到此时最后一个数字肯定是\(1\),所以\(M'=H_A\times H_A\times H_B\times G_A\times G_A\times M\)
    3. 让最后一个数\(-1\),然后在末尾加入两个\(1\)\(M'=H_A\times H_A\times H_A\times G_B \times G_A\times M\)

    然后我们会愉快的发现后两个和\(E\)有关的操作有\(H_A\times H_A\times H_B\times G_A\times G_A=H_A\times H_A\times H_A\times G_B \times G_A\)

    也就是其实\(E\)两种情况的操作乘上的都是同一个矩阵,那么用Splay维护即可,区间翻转和区间取反操作可以直接维护四个值,表示是否取反/翻转后的矩阵就行了。

    至于输出\(x\%P,y\%P\)\(gcd(x,y)\)\(1\)的要求我们不用担心,因为上面的操作中\(x,y\)\(gcd\)都是不会变的(因为\(gcd(x+y,x)=gcd(x,y)\)),所以无论怎样操作\(x,y\)都是互质的。

    时间复杂度:\(O((n+q)\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    const int N=2e5+10,S=2,P=998244353;
    int read(){
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    	return x*f;
    }
    void print(int x)
    {if(x>9)print(x/10);putchar('0'+x%10);}
    struct Matrix{
    	int a[S][S];
    }Ha,Hb,Ga,Gb,HA,HB,HC,O,c;
    void add(int &x,int y)
    {x=(x+y>=P)?(x+y-P):(x+y);}
    Matrix operator*(const Matrix &a,const Matrix &b){
    	memset(c.a,0,sizeof(c.a));
    	for(int i=0;i<S;i++)
    		for(int j=0;j<S;j++)
    			for(int k=0;k<S;k++)
    				add(c.a[i][j],1ll*a.a[i][k]*b.a[k][j]%P);
    	return c;
    }
    struct node{
    	Matrix M,N;
    	void flp(){swap(M,N);return;}
    }W,E,tmp;
    int n,q,cnt,root;
    char s[N];
    struct PeTree{
    	node w[N],v[N],a[N];
    	int t[N][2],fa[N],siz[N];
    	bool r[N],u[N];
    	void PushUp(int x){
    		w[x]=v[x]=a[x];
    		if(t[x][1]){
    			w[x].M=w[t[x][1]].M*w[x].M;
    			v[x].M=v[x].M*v[t[x][1]].M;
    			w[x].N=w[t[x][1]].N*w[x].N;
    			v[x].N=v[x].N*v[t[x][1]].N;
    		}
    		if(t[x][0]){
    			w[x].M=w[x].M*w[t[x][0]].M;
    			v[x].M=v[t[x][0]].M*v[x].M;
    			w[x].N=w[x].N*w[t[x][0]].N;
    			v[x].N=v[t[x][0]].N*v[x].N;
    		}
    		siz[x]=siz[t[x][0]]+siz[t[x][1]]+1;
    		return;
    	}
    	void PushR(int x)
    	{swap(t[x][0],t[x][1]);swap(w[x],v[x]);r[x]^=1;return;}
    	void PushU(int x)
    	{w[x].flp();v[x].flp();a[x].flp();u[x]^=1;return;}
    	void PushDown(int x){
    		if(r[x])PushR(t[x][0]),PushR(t[x][1]),r[x]=0;
    		if(u[x])PushU(t[x][0]),PushU(t[x][1]),u[x]=0;
    		return;
    	}
    	bool Direct(int x){return t[fa[x]][1]==x;}
    	void Rotate(int x){
    		int y=fa[x],z=fa[y];
    		int xs=Direct(x),ys=Direct(y);
    		int w=t[x][xs^1];
    		if(z)t[z][ys]=x;
    		t[x][xs^1]=y;t[y][xs]=w;
    		if(w)fa[w]=y;fa[y]=x;fa[x]=z;
    		PushUp(y);PushUp(x);return;
    	}
    	void Downdata(int x,int f){
    		if(x==f)return;
    		Downdata(fa[x],f);
    		PushDown(x);
    		return;
    	} 
    	void Splay(int x,int f){
    		if(!f)root=x;
    		Downdata(x,f);
    		while(fa[x]!=f){
    			int y=fa[x];
    			if(fa[y]==f)Rotate(x);
    			else if(Direct(x)==Direct(y))
    				Rotate(y),Rotate(x);
    			else Rotate(x),Rotate(x);
    		}
    		return;
    	}
    	int Find(int val,int x=root){
    		PushDown(x);
    		if(siz[t[x][0]]>=val)return Find(val,t[x][0]);
    		if(siz[t[x][0]]+1==val)return x;
    		return Find(val-siz[t[x][0]]-1,t[x][1]);
    	}
    }T;
    int power(int x,int b){
    	int ans=1;
    	while(b){
    		if(b&1)ans=1ll*ans*x%P;
    		x=1ll*x*x%P;b>>=1;
    	}
    	return ans;
    }
    void Query(){
    	T.Splay(1,0);
    	T.Splay(n+2,1);
    	Matrix ans=T.w[T.t[n+2][0]].M;
    	int a=ans.a[0][0];
    	int b=ans.a[0][1];
    	swap(a,b);a+=b;
    	a=(a%P+P)%P;b=(b+P)%P;
    	print(b);putchar(' ');
    	print(a);putchar('\n');
    }
    signed main()
    {
    	n=read();q=read();
    	
    	O.a[0][0]=1;O.a[1][0]=0;
    	O.a[1][0]=0;O.a[1][1]=1;
    	
    	Ha.a[0][0]=1;Ha.a[1][0]=1;
    	Ha.a[0][1]=0;Ha.a[1][1]=1;
    	
    	Ga.a[0][0]=1;Ga.a[1][0]=P-1;
    	Ga.a[0][1]=0;Ga.a[1][1]=1;
    	
    	Hb.a[0][0]=1;Hb.a[1][0]=1;
    	Hb.a[0][1]=1;Hb.a[1][1]=0;
    	
    	Gb.a[0][0]=0;Gb.a[1][0]=1;
    	Gb.a[0][1]=1;Gb.a[1][1]=P-1;
    	
    	HA=Hb*Ha*Gb;
    	HB=Hb*Hb*Ha*Gb*Gb;
    	HC=Hb*Hb*Hb*Ga*Gb;
    	
    	W=(node){HA,HB};
    	E=(node){HB,HA};
    	
    	scanf("%s",s+1);cnt=n+2;
    	T.t[1][1]=2;T.fa[n+2]=n+1;
    	for(int i=2;i<=n+1;i++){
    		if(s[i-1]=='W')T.a[i]=W;
    		else T.a[i]=E;
    		T.t[i][1]=i+1;
    		T.fa[i]=i-1;
    	}
    	for(int i=n+2;i>=1;i--)T.PushUp(i);
    	Query();int m=n;
    	while(q--){
    		char op[10];
    		scanf("%s",op);
    		if(op[0]=='A'){
    			scanf("%s",op);++cnt;
    			if(op[0]=='W')T.a[cnt]=W;
    			else T.a[cnt]=E;
    			int p=T.Find(m+1);
    			T.Splay(p,0);p=n+2;
    			T.fa[cnt]=p;T.t[p][0]=cnt;
    			T.PushUp(cnt);T.PushUp(p);++m;
    		}
    		else if(op[0]=='F'){
    			int l=read(),r=read();
    			l=T.Find(l);r=T.Find(r+2);
    			T.Splay(l,0);T.Splay(r,l);
    			T.PushU(T.t[r][0]);
    			T.Splay(T.t[r][0],0);
    		}
    		else if(op[0]=='R'){
    			int l=read(),r=read();
    			l=T.Find(l);r=T.Find(r+2);
    			T.Splay(l,0);T.Splay(r,l);
    			T.PushR(T.t[r][0]);
    			T.Splay(T.t[r][0],0);
    		}
    		Query();
    	}
    	return 0;
    }
    
  • 相关阅读:
    JS实现前台表格排序功能
    openoffice安装手记
    OpenOffice 实现OFFICE在线预览
    毫秒事件转换小方法
    axis2 jar包详解及缺少jar包异常分析
    Android EditText控件完美实现只读(ReadOnly/NonEditable)
    android:获取联系人信息(姓名和电话)
    dex2jar.bat反编译apk的classes.dex文件错误:
    服务器压力测试系列二:服务器监控工具tsar安装
    memcache list all keys
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16384409.html
Copyright © 2020-2023  润新知