CF633H Fibonacci-ish II 题解

题目传送门

前置知识

普通莫队算法 | 线段树 | 矩阵

解法

考虑莫队,先对原序列进行离散化。

考虑新加入一个数时在权值线段树将后面的数的斐波那契矩阵都乘上一个转移矩阵的系数来表示斐波那契下标加一,删除时将后面的数的斐波那契矩阵都除上一个转移矩阵的系数来表示斐波那契下标减一。

对于常见写法 \(\begin{bmatrix} Fib_{n} & Fib_{n+1} \\ 0 & 0 \end{bmatrix}=\begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix} \times \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}^{n}\),我们发现 \(\begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix}\) 没有逆矩阵,只能另行他法尝试删去其中的 \(0\)

另一种写法是 \(\begin{bmatrix} Fib_{n+1} & Fib_{n} \\ Fib_{n} & Fib_{n-1} \end{bmatrix}=\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} \times \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n}\),而 \(\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}\) 的逆矩阵是它本身,\(\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}\) 的逆矩阵是 \(\begin{bmatrix} 0 & 1 \\ 1 &-1 \end{bmatrix}\)。这样就解决了乘/除一个系数的问题。

略带卡常。需要取模优化和预处理斐波那契及其逆矩阵。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
int a[30010],b[30010],c[30010],pos[30010],L[30010],R[30010],ans[30010],cnt[30010],klen,ksum,p;
struct ask
{
	int l,r,id;
}q[30010];
bool q_cmp(ask a,ask b)
{
	return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l);
}
struct Matrix
{
	int ma[4];
	Matrix()
	{
		memset(ma,0,sizeof(ma));
	}
	Matrix operator + (const Matrix &another)
	{
		Matrix ans;
		ans.ma[0]=(ma[0]+another.ma[0])%p;
		ans.ma[1]=(ma[1]+another.ma[1])%p;
		ans.ma[2]=(ma[2]+another.ma[2])%p;
		ans.ma[3]=(ma[3]+another.ma[3])%p;
		return ans;
	}
	Matrix operator * (const Matrix &another)
	{
		Matrix ans;
		ans.ma[0]=(ma[0]*another.ma[0]+ma[1]*another.ma[2])%p;
		ans.ma[1]=(ma[0]*another.ma[1]+ma[1]*another.ma[3])%p;
		ans.ma[2]=(ma[2]*another.ma[0]+ma[3]*another.ma[2])%p;
		ans.ma[3]=(ma[2]*another.ma[1]+ma[3]*another.ma[3])%p;
		return ans;
	}
	Matrix operator * (const int &another)
	{
		Matrix ans;
		ans.ma[0]=ma[0]*another;
		ans.ma[1]=ma[1]*another;
		ans.ma[2]=ma[2]*another;
		ans.ma[3]=ma[3]*another;
		return ans;
	}
}Base,invBase,Fib[30010],invFib[30010];
struct SMT
{
	struct SegmentTree
	{
		int val,lazy;
		Matrix sum;
	}tree[120010];
	#define lson(rt) (rt<<1)
	#define rson(rt) (rt<<1|1)
	void pushup(int rt)
	{
		tree[rt].sum=(tree[lson(rt)].sum*tree[lson(rt)].val)+(tree[rson(rt)].sum*tree[rson(rt)].val);
	}
	void build(int rt,int l,int r)
	{
		if(l==r)
		{
			tree[rt].val=0;
			tree[rt].sum=Base;
			return;
		}
		tree[rt].val=1;
		int mid=(l+r)>>1;
		build(lson(rt),l,mid);
		build(rson(rt),mid+1,r);
	}
	void pushdown(int rt)
	{
		if(tree[rt].lazy!=0)
		{
			if(tree[rt].lazy>0)
			{
				tree[lson(rt)].sum=tree[lson(rt)].sum*Fib[tree[rt].lazy];
				tree[rson(rt)].sum=tree[rson(rt)].sum*Fib[tree[rt].lazy];
			}
			else
			{
				tree[lson(rt)].sum=tree[lson(rt)].sum*invFib[-tree[rt].lazy];
				tree[rson(rt)].sum=tree[rson(rt)].sum*invFib[-tree[rt].lazy];
			}
			tree[lson(rt)].lazy+=tree[rt].lazy;
			tree[rson(rt)].lazy+=tree[rt].lazy;
			tree[rt].lazy=0;
		}
	}
	void update1(int rt,int l,int r,int pos,int val)
	{
		if(l==r)
		{
			tree[rt].val=val;
			return;
		}
		pushdown(rt);
		int mid=(l+r)>>1;
		if(pos<=mid)
		{
			update1(lson(rt),l,mid,pos,val);
			tree[rson(rt)].sum=tree[rson(rt)].sum*Base;
			tree[rson(rt)].lazy++;
		}
		else
		{
			update1(rson(rt),mid+1,r,pos,val);
		}
		pushup(rt);
	}
	void update2(int rt,int l,int r,int pos,int val)
	{
		if(l==r)
		{
			tree[rt].val=val;
			return;
		}
		pushdown(rt);
		int mid=(l+r)>>1;
		if(pos<=mid)
		{
			update2(lson(rt),l,mid,pos,val);
			tree[rson(rt)].sum=tree[rson(rt)].sum*invBase;
			tree[rson(rt)].lazy--;
		}
		else
		{
			update2(rson(rt),mid+1,r,pos,val);
		}
		pushup(rt);
	}
}T;
void init(int n,int m)
{
	klen=n/sqrt(m)+1;
	ksum=n/klen;
	for(int i=1;i<=ksum;i++)
	{
		L[i]=R[i-1]+1;
		R[i]=R[i-1]+klen;
	}
	if(R[ksum]<n)
	{
		ksum++;
		L[ksum]=R[ksum-1]+1;
		R[ksum]=n;
	}
	for(int i=1;i<=ksum;i++)
	{
		for(int j=L[i];j<=R[i];j++)
		{
			pos[j]=i;
		}
	}
	Base.ma[0]=1;     Base.ma[1]=1;     Base.ma[2]=1;     Base.ma[3]=0;
	invBase.ma[0]=0;  invBase.ma[1]=1;  invBase.ma[2]=1;  invBase.ma[3]=p-1;
	Fib[0].ma[0]=1;   Fib[0].ma[1]=0;   Fib[0].ma[2]=0;   Fib[0].ma[3]=1;
	invFib[0].ma[0]=1;invFib[0].ma[1]=0;invFib[0].ma[2]=0;invFib[0].ma[3]=1;
	for(int i=1;i<=n;i++)
	{
		Fib[i]=Fib[i-1]*Base;
		invFib[i]=invFib[i-1]*invBase;
	}
	T.build(1,1,b[0]);
}
void add(int x)
{
	cnt[a[x]]++;
	if(cnt[a[x]]==1)
	{
		T.update1(1,1,b[0],a[x],c[x]);
	}
}
void del(int x)
{
	cnt[a[x]]--;
	if(cnt[a[x]]==0)
	{
		T.update2(1,1,b[0],a[x],0);
	}
}
int main()
{
	int n,m,l,r,i;
	scanf("%d%d",&n,&p);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
		c[i]=a[i]%p;
	}
	sort(b+1,b+1+n);
	b[0]=unique(b+1,b+1+n)-(b+1);
	for(i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
	}
	scanf("%d",&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	init(n,m);
	sort(q+1,q+1+m,q_cmp);
	for(i=1,l=1,r=0;i<=m;i++)
	{
		while(l>q[i].l)
		{
			l--;
			add(l);
		}
		while(r<q[i].r)
		{
			r++;
			add(r);
		}
		while(l<q[i].l)
		{
			del(l);
			l++;
		}
		while(r>q[i].r)
		{
			del(r);
			r--;
		}
		ans[q[i].id]=T.tree[1].sum.ma[1];
	}
	for(int i=1;i<=m;i++)
	{
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2024-11-14 14:29  hzoi_Shadow  阅读(13)  评论(1编辑  收藏  举报
扩大
缩小