[NOI2021] 密码箱

\(\text{Problem}:\)[NOI2021] 密码箱

\(\text{Solution}:\)

首先,不难发现答案的分子和分母一定是互质的。具体的,考虑当前的答案为 \(\dfrac{x}{y}\)​,新加入一个 \(k\)​,答案变为 \(\dfrac{y+xk}{x}\)​,而显然 \(\gcd(x,y+xk)=\gcd(x,y)\)​。

用向量维护当前的答案 \(\dfrac{x}{y}\)​​​,当从左边加入一个 \(k\)​ 时,易得 \(\left[\begin{array}{ccc}x&y \end{array}\right] \times \left[\begin{array}{ccc}k&1 \\ 1&0\end{array}\right]=\left[\begin{array}{ccc}y+xk&x\end{array}\right]\)​​​。而 APPEND 是在序列末尾加操作,即操作顺序是从序列末尾向左,而每次操作均为右乘

设每次操作在末尾加入一个矩阵 \(p\)。最后加入一个 W 操作时,则有 \(p\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]=\left[\begin{array}{ccc}k+1&1\\1&0\end{array}\right]\),易得 \(p=\left[\begin{array}{ccc}1&1\\0&1\end{array}\right]\)。最后加入一个 E 操作时,需要分类讨论:

  • 若序列末尾为 \(1\),则 \(p\times \left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]=\left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k+1&1\\1&0\end{array}\right]\),得 \(p=\left[\begin{array}{ccc}2&-1\\1&0\end{array}\right]\)
  • 若序列末尾不为 \(1\),则 \(p\times \left[\begin{array}{ccc}x&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]=\left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}x-1&1\\1&0\end{array}\right]\times \left[\begin{array}{ccc}k&1\\1&0\end{array}\right]\),得 \(p=\left[\begin{array}{ccc}2&-1\\1&0\end{array}\right]\)​。

以上,发现对于 E 操作的两种情况对应的矩阵是相同的,故无需分类讨论。

考虑有区间反转和区间翻转的操作,容易联想到用平衡树去维护操作序列。为了方便求出答案,需要对每个结点 \(x\) 维护:\(x\) 是否存在反转操作;\(x\) 是否存在翻转操作;\(x\)​ 对应序列的前缀与后缀及其反转序列的前缀与后缀答案。总时间复杂度 \(O(n\log n)\)

\(\text{Code}:\)

#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=300010, Mod=998244353;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
} char inp[23];
int n,m;
char s[N];
int root,cnt,ch[N][2],rnd[N],siz[N],kd[N],rev[N],flip[N];
struct Mat { int a[2][2]; inline Mat () { memset(a,0,sizeof(a)); } };
inline Mat operator * (Mat x,Mat y)
{
	Mat res;
	for(ri int i=0;i<2;i++)
	for(ri int k=0;k<2;k++)
	for(ri int j=0;j<2;j++)
	res.a[i][j]=(res.a[i][j]+1ll*x.a[i][k]*y.a[k][j]%Mod)%Mod;
	return res;
}
Mat sum[N][4],val[N][2],bw,be,bas;
inline int New_Node(int tp)
{
	rnd[++cnt]=rand();
	siz[cnt]=1;
	if(tp) val[cnt][0]=bw, val[cnt][1]=be;
	else val[cnt][0]=be, val[cnt][1]=bw;
	sum[cnt][0]=sum[cnt][1]=val[cnt][0], sum[cnt][2]=sum[cnt][3]=val[cnt][1];
	return cnt;
}
inline void Push_Up(int x)
{
	siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
	sum[x][0]=sum[ch[x][0]][0]*val[x][0]*sum[ch[x][1]][0];
	sum[x][1]=sum[ch[x][1]][1]*val[x][0]*sum[ch[x][0]][1];
	sum[x][2]=sum[ch[x][0]][2]*val[x][1]*sum[ch[x][1]][2];
	sum[x][3]=sum[ch[x][1]][3]*val[x][1]*sum[ch[x][0]][3];
}
inline void Push_Down(int x)
{
	if(rev[x])
	{
		swap(sum[ch[x][0]][0],sum[ch[x][0]][1]);
		swap(sum[ch[x][0]][2],sum[ch[x][0]][3]);
		swap(sum[ch[x][1]][0],sum[ch[x][1]][1]);
		swap(sum[ch[x][1]][2],sum[ch[x][1]][3]);
		swap(ch[ch[x][0]][0],ch[ch[x][0]][1]);
		swap(ch[ch[x][1]][0],ch[ch[x][1]][1]);
		if(ch[x][0]) rev[ch[x][0]]^=1;
		if(ch[x][1]) rev[ch[x][1]]^=1;
		rev[x]=0;
	}
	if(flip[x])
	{
		swap(sum[ch[x][0]][0],sum[ch[x][0]][2]);
		swap(sum[ch[x][0]][1],sum[ch[x][0]][3]);
		swap(sum[ch[x][1]][0],sum[ch[x][1]][2]);
		swap(sum[ch[x][1]][1],sum[ch[x][1]][3]);
		swap(val[ch[x][0]][0],val[ch[x][0]][1]);
		swap(val[ch[x][1]][0],val[ch[x][1]][1]);
		if(ch[x][0]) flip[ch[x][0]]^=1;
		if(ch[x][1]) flip[ch[x][1]]^=1;
		flip[x]=0;
	}
}
void Split(int root,int k,int &x,int &y)
{
	if(!root) { x=y=0; return; }
	Push_Down(root);
	if(siz[ch[root][0]]<k) x=root, Split(ch[root][1],k-siz[ch[root][0]]-1,ch[root][1],y);
	else y=root, Split(ch[root][0],k,x,ch[root][0]);
	Push_Up(root);
}
int Merge(int x,int y)
{
	if(!x||!y) return x|y;
	if(rnd[x]<rnd[y])
	{
		Push_Down(x);
		ch[x][1]=Merge(ch[x][1],y);
		Push_Up(x);
		return x;
	}
	else
	{
		Push_Down(y);
		ch[y][0]=Merge(x,ch[y][0]);
		Push_Up(y);
		return y;
	}
}
inline void Insert(int tp)
{
	root=Merge(root,New_Node(tp));
}
inline void Flip(int l,int r)
{
	int x,y,z;
	Split(root,l-1,x,y);
	Split(y,r-l+1,y,z);
	flip[y]^=1;
	swap(val[y][0],val[y][1]);
	swap(sum[y][0],sum[y][2]);
	swap(sum[y][1],sum[y][3]);
	root=Merge(x,Merge(y,z));
}
inline void Reverse(int l,int r)
{
	int x,y,z;
	Split(root,l-1,x,y);
	Split(y,r-l+1,y,z);
	rev[y]^=1;
	swap(sum[y][0],sum[y][1]);
	swap(sum[y][2],sum[y][3]);
	swap(ch[y][0],ch[y][1]);
	root=Merge(x,Merge(y,z));
}
signed main()
{
	srand(time(NULL));
	n=read(), m=read();
	scanf("%s",s+1);
	bw.a[0][0]=1, bw.a[0][1]=1, bw.a[1][1]=1;
	be.a[0][0]=2, be.a[0][1]=Mod-1, be.a[1][0]=1;
	for(ri int i=0;i<4;i++) sum[0][i].a[0][0]=sum[0][i].a[1][1]=1;
	for(ri int i=1;i<=n;i++)
	{
		Insert(s[i]=='W');
	}
	Mat ans=sum[root][1]*bw;
	printf("%d %d\n",ans.a[0][0],ans.a[0][1]);
	for(ri int i=1;i<=m;i++)
	{
		scanf("%s",inp);
		if(inp[0]=='A')
		{
			scanf("%s",inp);
			Insert(inp[0]=='W');
		}
		else if(inp[0]=='F')
		{
			int l,r;
			l=read(), r=read();
			Flip(l,r);
		}
		else
		{
			int l,r;
			l=read(), r=read();
			Reverse(l,r);
		}
		Mat ans=sum[root][1]*bw;
		printf("%d %d\n",ans.a[0][0],ans.a[0][1]);
	}
	return 0;
}
posted @ 2021-07-31 11:48  zkdxl  阅读(236)  评论(0编辑  收藏  举报