ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

YbtOJ#732-斐波那契【特征方程,LCT】

2021-02-26 19:58:06  阅读:185  来源: 互联网

标签:node pre LCT return YbtOJ ll fa ans 特征方程


正题

题目链接:http://www.ybtoj.com.cn/contest/125/problem/2


题目大意

给出 n n n个点的一棵树,以 1 1 1为根,每个点有点权 a i a_i ai​。要求支持 m m m次操作

  1. 修改一个修改一个节点的父节点
  2. 修改一条路径的权值为 w w w
  3. 给出 u u u询问 F b i ( a u ) Fbi(a_u) Fbi(au​)
  4. 给出 u , v u,v u,v,将路径 u − > v u->v u−>v的点权排列好后设为 b b b求
    ∑ i = 1 k ∑ j = i k F b i ( ∑ z = i j b z ) \sum_{i=1}^k\sum_{j=i}^kFbi(\sum_{z=i}^jb_z) i=1∑k​j=i∑k​Fbi(z=i∑j​bz​)

其中 F b i ( i ) Fbi(i) Fbi(i)表示第 i i i个斐波那契数。输出答案模 998244353 998244353 998244353的值

1 ≤ n , m ≤ 1 0 5 , a i , w ∈ [ 1 , 1 0 9 ] 1\leq n,m\leq 10^5,a_i,w\in[1,10^9] 1≤n,m≤105,ai​,w∈[1,109]


解题思路

嗯这个斐波那契很麻烦,可以考虑一些用特征方程 1 − x − x 2 = 0 1-x-x^2=0 1−x−x2=0,可以得到斐波那契的通项公式
F b i ( n ) = ( 5 + 1 2 ) n − ( 5 − 1 2 ) n 5 Fbi(n)=\frac{(\frac{\sqrt 5+1}{2})^n-(\frac{\sqrt 5-1}{2})^n}{\sqrt 5} Fbi(n)=5 ​(25 ​+1​)n−(25 ​−1​)n​
为了方便上面 5 ± 1 2 \frac{\sqrt 5\pm 1}{2} 25 ​±1​分别记为 X 0 , X 1 X_0,X_1 X0​,X1​。
那么如果设 c i = X 0 a i , d i = X 1 a i c_i=X_0^{a_i},d_i=X_1^{a_i} ci​=X0ai​​,di​=X1ai​​的话我们要求的就是
∑ i = 1 k ∑ j = i k ∏ z = i j c z − ∑ i = 1 k ∑ j = i k ∏ z = i j d z 5 \frac{\sum_{i=1}^k\sum_{j=i}^k\prod_{z=i}^jc_z-\sum_{i=1}^k\sum_{j=i}^k\prod_{z=i}^jd_z}{\sqrt 5} 5 ​∑i=1k​∑j=ik​∏z=ij​cz​−∑i=1k​∑j=ik​∏z=ij​dz​​
这个好像看起来好维护一点,不过首先我们要解决这个 5 \sqrt 5 5 ​的问题,因为其实 5 \sqrt 5 5 ​在模 998244353 998244353 998244353意义下是没有值的,我们不能直接用二次剩余带入数字。

考虑维护一个类似于多项式的东西,每个数字记为二元组 ( a , b ) = a 5 + b (a,b)=a\sqrt 5+b (a,b)=a5 ​+b。加减乘都很好搞,除法的话需要推导一下,
1 a 5 + b = c 5 + d \frac{1}{a\sqrt 5+b}=c\sqrt 5+d a5 ​+b1​=c5 ​+d
5 a c + 5 ( a d + c b ) + b d = 1 5ac+\sqrt 5(ad+cb)+bd=1 5ac+5 ​(ad+cb)+bd=1
5 a c + b d = 1 , a d + c b = 0 5ac+bd=1,ad+cb=0 5ac+bd=1,ad+cb=0
解出来 c = − a b 2 − 5 a 2 , d = b b 2 − 5 a 2 c=-\frac{a}{b^2-5a^2},d=\frac{b}{b^2-5a^2} c=−b2−5a2a​,d=b2−5a2b​
这样四则运算都搞定了,可以开始考虑如何在 L C T LCT LCT上面维护了。

类似线段树的,设 p r o pro pro表示所有数乘积, p r e / s u f pre/suf pre/suf表示所有前/后缀乘积和, a n s ans ans表示我们维护的答案,那么就可以合并两个东西了。 L C T LCT LCT维护的时候顺便把单个的节点也合并进去就好了。

然后还剩下一个最麻烦的东西就是树链修改的时候我们需要快速算出连续 x x x个 u u u的信息。
p r o pro pro很好搞就是 u x u^x ux, s u f suf suf和 p r e pre pre就是一个简单的等比数列求和,上通项公式就好了。
a n s ans ans比较麻烦,考虑每个 u i u^i ui的个数答案就是
∑ i = 1 x u i ( x − i + 1 ) = ( x + 1 ) ∑ i = 1 x u i − ∑ i = 1 x u i i \sum_{i=1}^xu^i(x-i+1)=(x+1)\sum_{i=1}^xu^i-\sum_{i=1}^xu^ii i=1∑x​ui(x−i+1)=(x+1)i=1∑x​ui−i=1∑x​uii
⇒ ( x + 1 ) u x + 1 − u u − 1 − x u x + 1 − u x − u u − 1 u − 1 \Rightarrow (x+1)\frac{u^{x+1}-u}{u-1}-\frac{xu^{x+1}-\frac{u^x-u}{u-1}}{u-1} ⇒(x+1)u−1ux+1−u​−u−1xux+1−u−1ux−u​​
这样就可以在 l o g log log时间复杂度以内合并了。

然后答案 0 0 0次项一定是 0 0 0的,所以输出 5 \sqrt 5 5 ​的项就好了。
时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define ll long long
using namespace std;
const ll P=998244353,N=1e5+10;
struct node{
	ll a,b;//a带√5 
	node(ll aa=0,ll bb=0)
	{a=aa;b=bb;return;}
};
ll power(ll x,ll b=P-2){
	ll ans=1;x%=P;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
const node X((P+1)/2,(P+1)/2);
node operator+(node x,node y)
{return node((x.a+y.a)%P,(x.b+y.b)%P);}
node operator-(node x,node y)
{return node((x.a-y.a)%P,(x.b-y.b)%P);}
node operator*(node x,node y)
{return node((x.a*y.b+x.b*y.a)%P,(x.b*y.b+5*x.a*y.a)%P);}
node inv(node x){
	ll tmp=power(x.b*x.b-5*x.a*x.a);
	return node(-x.a,x.b)*node(0,tmp);
}
node power(node x,ll b){
	node ans(0,1);
	while(b){
		if(b&1)ans=ans*x;
		x=x*x;b>>=1;
	}
	return ans;
}
struct Tnode{
	node ans,pre,suf,pro;
};
Tnode operator+(Tnode x,Tnode y){
	Tnode w;
	w.ans=x.ans+y.ans+x.suf*y.pre;
	w.pre=x.pre+y.pre*x.pro;
	w.suf=y.suf+x.suf*y.pro;
	w.pro=x.pro*y.pro;return w;
}
struct SegTree{
	ll fa[N],t[N][2],siz[N];
	Tnode w[N];node v[N],lazy[N];
	bool r[N],hlz[N];stack<ll> s;
	bool Nroot(ll x)
	{return fa[x]&&(t[fa[x]][0]==x||t[fa[x]][1]==x);}
	bool Direct(ll x)
	{return t[fa[x]][1]==x;}
	void Rev(ll x)
	{swap(t[x][0],t[x][1]);swap(w[x].pre,w[x].suf);r[x]^=1;return;}
	void PushUp(ll x){
		siz[x]=siz[t[x][0]]+siz[t[x][1]]+1;
		w[x]=(Tnode){v[x],v[x],v[x],v[x]};
		if(t[x][0])w[x]=w[t[x][0]]+w[x];
		if(t[x][1])w[x]=w[x]+w[t[x][1]];
		return;
	}
	void Updata(ll x,node u){
		ll s=siz[x];lazy[x]=v[x]=u;
		node tmp=inv(node(0,1)-u);
		hlz[x]=1; w[x].pro=power(u,s);
		w[x].pre=w[x].suf=(u-w[x].pro*u)*tmp;
		w[x].ans=(node(0,s)-w[x].pre)*u*tmp;
		return;
	}
	void PushDown(ll x){
		if(hlz[x]){
			if(t[x][0])Updata(t[x][0],lazy[x]);
			if(t[x][1])Updata(t[x][1],lazy[x]);
			hlz[x]=0;
		}
		if(!r[x])return;
		Rev(t[x][0]);Rev(t[x][1]);
		r[x]=0;return;
	}
	void Rotate(ll x){
		ll y=fa[x],z=fa[y];
		ll xs=Direct(x),ys=Direct(y);
		ll w=t[x][xs^1];
		if(Nroot(y))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 Splay(ll x){
		ll y=x;s.push(x);
		while(Nroot(y))y=fa[y],s.push(y);
		while(!s.empty())PushDown(s.top()),s.pop();
		while(Nroot(x)){
			ll y=fa[x];
			if(!Nroot(y))Rotate(x);
			else if(Direct(x)==Direct(y))
				Rotate(y),Rotate(x);
			else Rotate(x),Rotate(x);
		}
		return;
	}
	void Access(ll x){
		for(ll y=0;x;y=x,x=fa[x])
			Splay(x),t[x][1]=y,PushUp(x);
		return;
	}
	void MakeRoot(ll x)
	{Access(x);Splay(x);Rev(x);return;}
	void Link(ll x,ll y){
		MakeRoot(1);Access(x);Splay(x);
		fa[t[x][0]]=0;t[x][0]=0;PushUp(x);
		fa[x]=y;return;
	}
	ll Split(ll x,ll y){
		MakeRoot(x);Access(y);Splay(y);
		return (w[y].ans.a+P)%P*2%P;
	}
	void Change(ll x,ll y,node val){
		MakeRoot(x);Access(y);Splay(y);
		Updata(y,val);return;
	}
}T;
ll n,m;
signed main()
{
//	freopen("fibonacci.in","r",stdin);
//	freopen("fibonacci.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=n;i++){
		ll x;scanf("%lld",&x);
		T.v[i]=power(X,x);
		T.PushUp(i);
	}
	for(ll i=2;i<=n;i++)
		scanf("%lld",&T.fa[i]);
	while(m--){
		ll op,u,v,w;
		scanf("%lld",&op);
		if(op==1){
			scanf("%lld%lld",&u,&v);
			T.Link(u,v);
		}
		else if(op==2){
			scanf("%lld%lld%lld",&u,&v,&w);
			T.Change(u,v,power(X,w));
		}
		else if(op==3){
			scanf("%lld",&u);
			printf("%lld\n",T.Split(u,u));
		}
		else if(op==4){
			scanf("%lld%lld",&u,&v);
			printf("%lld\n",T.Split(u,v));
		}
	}
	return 0;
}

标签:node,pre,LCT,return,YbtOJ,ll,fa,ans,特征方程
来源: https://blog.csdn.net/Mr_wuyongcong/article/details/114148869

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有