[NOI2021]密码箱

密码箱

题解

**平衡树,贞难调二世

由于题目要求答案必须是分数的最简,而在取模的意义下求 g c d gcd gcd又不大现实,我们先可以猜测我们直接求出来的答案一定是最简分数。
很容易发现,我们求答案的式子都是 a + y x a+\frac{y}{x} a+xy的形式。在 ( x , y ) = 1 (x,y)=1 (x,y)=1的情况下,很容易发现 ( a x + y , x ) = 1 (ax+y,x)=1 (ax+y,x)=1,所以我们求出的答案一定是最简的分数形式。

Point 1-4: 20pts
很容易想到的 O ( n q ) O\left(nq\right) O(nq)暴力修改查询,这就不详细说明了。
Point 5-7: 35pts
由于不会出现两个相邻的字符,所以我们的序列一定是 E W E W E W . . . EWEWEW... EWEWEW... W E W E W E . . . WEWEWE... WEWEWE...的形式。
经过暴力检验,我们可以发现它的答案一定是斐波拉契数。
于是,我们只需要维护第一个操作的种类。
对于 A p p e n d Append Append操作,它只会影响操作序列的长度。
对于 F l i p Flip Flip操作,只在针对整个序列反转时才有意义,会让我们第一个字符反转。
对于 R e v e r s e Reverse Reverse操作,它也只在针对整个序列的翻转时有意义,若序列长度为偶数会让第一个字符反转。
时间复杂度 O ( n + q ) O\left(n+q\right) O(n+q)
Point 8-10:50pts
我们可以考虑序列中的一个数会对答案造成怎样的影响, f ( a , x y ) = a x + y x f(a,\frac{x}{y})=\frac{ax+y}{x} f(a,yx)=xax+y
如果我们将分数看作一个数对显然是 ( x , y ) → ( a x + y , x ) (x,y)\rightarrow(ax+y,x) (x,y)(ax+y,x)
这显然可以被写成矩阵相乘的形式, ( x , y ) ( a , 1 1 , 0 ) = ( a x + y , x ) (x,y)\left(\begin{array}{cc}a,1\\1,0\end{array}\right)=(ax+y,x) (x,y)(a,11,0)=(ax+y,x)
而在只有 A p p e n d Append Append操作的情况下我们的 a a a序列是可以 O ( 1 ) O\left(1\right) O(1)维护的,它只会影响后面的几个 a a a值,同理我们也可以 O ( 1 ) O\left(1\right) O(1)维护后后面几个点的矩阵,乘起来得到答案。
时间复杂度 O ( w 3 ( n + q ) ) O\left(w^3(n+q)\right) O(w3(n+q))
Point 11-20:100pts
由上面的做法我们应该很快可以知道这道题可以利用矩阵解决,但很明显我们不能直接维护 a a a序列。
但是我们是知道 a a a序列的样子的,考虑每个操作会对其产生怎样的影响。
起初的 a a a序列 ( 0 , 1 ) (0,1) (0,1),而 W W W操作会让 a n a_{n} an加一,也就是让 ( a , 1 1 , 0 ) \left(\begin{array}{cc}a,1\\1,0\end{array}\right) (a,11,0)变成 ( a + 1 , 1 1 , 0 ) \left(\begin{array}{cc}a+1,1\\1,0\end{array}\right) (a+1,11,0)
很明显,这可以通过右乘矩阵 ( 1 , 0 1 , 1 ) \left(\begin{array}{cc}1,0\\1,1\end{array}\right) (1,01,1)来解决。
B B B操作的两种情况 ( a + 1 , b ) (a+1,b) (a+1,b), ( a , b − 1 , 1 , 1 ) (a,b-1,1,1) (a,b1,1,1)实际上得到的答案在 b = 1 b=1 b=1的时候都是一样的,而我们也只有在 b = 1 b=1 b=1的情况下会使 a + + a++ a++,还不如都将其看作 ( a , b ) ⇒ ( a , b − 1 , 1 , 1 ) (a,b)\Rightarrow(a,b-1,1,1) (a,b)(a,b1,1,1)的操作。
而该操作我们可以看成乘 3 3 3个矩阵 ( 1 , 0 − 1 , 1 ) ( 1 , 1 1 , 0 ) ( 1 , 1 1 , 0 ) = ( 2 , 1 − 1 , 0 ) \left(\begin{array}{cc}1,0\\-1,1\end{array}\right)\left(\begin{array}{cc}1,1\\1,0\end{array}\right)\left(\begin{array}{cc}1,1\\1,0\end{array}\right)=\left(\begin{array}{cc}2,1\\-1,0\end{array}\right) (1,01,1)(1,11,0)(1,11,0)=(2,11,0)
于是我们所有的操作都相当于乘上一个矩阵。
而我们的 A p p e n d Append Append操作相当与多乘一个矩阵, F l i p Flip Flip矩阵相当于将 W W W操作的矩阵与 B B B操作的矩阵调换, R e v e r s e Reverse Reverse操作相当于将一个区间内矩阵乘的顺序反转。
这些从操作都可以通过平衡树来进行维护,但由于踏实区间操作,我们只能通过懒标记的下传来更新,不能直接更改,所以对于一个区间我们要维护 4 4 4个矩阵,不同种类以及不同顺序相乘得到的矩阵。
每次交换和反转可以直接通过交换这四个矩阵来实现快速更新,方便懒标记的下传。
最后乘上初始序列的 { a 0 , a 1 } \{a_0,a_1\} {a0,a1}的矩阵 ( 1 , 0 1 , 1 ) \left(\begin{array}{cc}1,0\\1,1\end{array}\right) (1,01,1)即可。

时间复杂度 O ( w 3 q l o g   n ) O\left(w^3qlog\,n\right) O(w3qlogn)

源码

调了好久

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int mo=998244353;
const int jzm=2333;
const int lim=10000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
struct matrix{
	int c[3][3];matrix(){memset(c,0,sizeof(c));}
	matrix operator * (const matrix &rhs)const{
		matrix res;
		for(int i=1;i<3;i++)
			for(int k=1;k<3;k++)
				for(int j=1;j<3;j++)
					res.c[i][j]=add(res.c[i][j],1ll*c[i][k]*rhs.c[k][j]%mo,mo);
		return res;
	}
	void print(){
		for(int i=1;i<3;i++,puts(""))
			for(int j=1;j<3;j++)printf("%d ",c[i][j]);
		puts("");
	}
}Ia,Ib,Id;
int n,q;char str[MAXN];
struct ming{
	int ch[2],siz,rnd;matrix d[4],vala,valb;bool rev,lzy;
	ming(){ch[0]=ch[1]=siz=rnd=rev=lzy=0;}
};
class FHQ_Treap{
	private:
		ming tr[MAXN];int tot,root;
		int newnode(int typ){
			int rt=++tot;tr[rt].siz=1;tr[rt].rnd=rand();
			tr[rt].vala=tr[rt].d[0]=tr[rt].d[2]=(typ?Ia:Ib);
			tr[rt].valb=tr[rt].d[1]=tr[rt].d[3]=(typ?Ib:Ia);
			return rt; 
		}
		void pushup(int rt){
			tr[rt].siz=1+tr[tr[rt].ch[0]].siz+tr[tr[rt].ch[1]].siz;tr[rt].d[0]=tr[rt].d[2]=tr[rt].vala;tr[rt].d[1]=tr[rt].d[3]=tr[rt].valb;
			if(tr[rt].ch[0])
				tr[rt].d[0]=tr[tr[rt].ch[0]].d[0]*tr[rt].d[0],tr[rt].d[1]=tr[tr[rt].ch[0]].d[1]*tr[rt].d[1],
				tr[rt].d[2]=tr[rt].d[2]*tr[tr[rt].ch[0]].d[2],tr[rt].d[3]=tr[rt].d[3]*tr[tr[rt].ch[0]].d[3];
			if(tr[rt].ch[1])
				tr[rt].d[0]=tr[rt].d[0]*tr[tr[rt].ch[1]].d[0],tr[rt].d[1]=tr[rt].d[1]*tr[tr[rt].ch[1]].d[1],
				tr[rt].d[2]=tr[tr[rt].ch[1]].d[2]*tr[rt].d[2],tr[rt].d[3]=tr[tr[rt].ch[1]].d[3]*tr[rt].d[3];
		}
		void Flip(int rt){swap(tr[rt].vala,tr[rt].valb);swap(tr[rt].d[0],tr[rt].d[1]);swap(tr[rt].d[2],tr[rt].d[3]);tr[rt].lzy^=1;}
		void Reverse(int rt){swap(tr[rt].ch[0],tr[rt].ch[1]);swap(tr[rt].d[0],tr[rt].d[2]);swap(tr[rt].d[1],tr[rt].d[3]);tr[rt].rev^=1;}
		void pushdown(int rt){
			if(tr[rt].lzy){if(tr[rt].ch[0])Flip(tr[rt].ch[0]);if(tr[rt].ch[1])Flip(tr[rt].ch[1]);tr[rt].lzy=0;}
			if(tr[rt].rev){if(tr[rt].ch[0])Reverse(tr[rt].ch[0]);if(tr[rt].ch[1])Reverse(tr[rt].ch[1]);tr[rt].rev=0;}
		}
		void split(int now,int k,int &x,int &y){
			if(!now){x=y=0;return ;}pushdown(now);
			if(k>tr[tr[now].ch[0]].siz)
				x=now,split(tr[now].ch[1],k-tr[tr[now].ch[0]].siz-1,tr[x].ch[1],y),pushup(x);
			else y=now,split(tr[now].ch[0],k,x,tr[y].ch[0]),pushup(y);
		}
		int merge(int a,int b){
			if(!a||!b)return a+b;
			pushdown(a);pushdown(b);
			if(tr[a].rnd<tr[b].rnd){
				tr[a].ch[1]=merge(tr[a].ch[1],b);
				pushup(a);return a;
			}
			tr[b].ch[0]=merge(a,tr[b].ch[0]);
			pushup(b);return b;
		}
		void build(int &rt,int l,int r){
			int mid=l+r>>1;rt=newnode(str[mid]=='W');
			if(l<mid)build(tr[rt].ch[0],l,mid-1);
			if(r>mid)build(tr[rt].ch[1],mid+1,r);
			pushup(rt);
		}
	public:
		void Build(int len){build(root,1,len);}
		void Append(int typ){root=merge(root,newnode(typ));}
		void MakeFlip(int l,int r){
			int x1,y1,x2,y2;split(root,r,x1,y1);
			split(x1,l-1,x2,y2);Flip(y2);
			root=merge(merge(x2,y2),y1);
		}
		void MakeReverse(int l,int r){
			int x1,y1,x2,y2;split(root,r,x1,y1);
			split(x1,l-1,x2,y2);Reverse(y2);
			root=merge(merge(x2,y2),y1);
		}
		pii Query(){
			matrix res=Id*tr[root].d[0];
			return mkpr(res.c[1][1],res.c[2][1]);
		}
}T; 
signed main(){
	//freopen("code.in","r",stdin);
	//freopen("code.out","w",stdout);
	srand(114514);read(n);read(q);
	Id.c[1][1]=1;Id.c[2][1]=1;Id.c[2][2]=1;
	Ia.c[1][1]=1;Ia.c[2][2]=1;Ia.c[2][1]=1;
	Ib.c[1][1]=2;Ib.c[2][1]=mo-1;Ib.c[1][2]=1;
	scanf("\n%s",str+1);T.Build(n);
	pii res=T.Query();printf("%d %d\n",res.fir,res.sec);
	for(int i=1;i<=q;i++){
		char opt[10]={},ch;int l,r;scanf("\n%s",opt+1);
		if(opt[1]=='A')scanf(" %c",&ch),T.Append(ch=='W');
		else if(opt[1]=='F')read(l),read(r),T.MakeFlip(l,r);
		else read(l),read(r),T.MakeReverse(l,r);
		pii res=T.Query();printf("%d %d\n",res.fir,res.sec);
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值