1. 程式人生 > 實用技巧 >習題:Sasha and Array(線段樹)

習題:Sasha and Array(線段樹)

題目

傳送門

思路

看見區間操作比較容易聯想到線段樹

考慮怎麼對於一個節點進行快速計算

對於葉子節點,存下\(f_{a_i-1},f_{a_i}\),每一個節點表示\(\sum_{i=l}^{r}f_{a_i-1},\sum_{i=l}^{r}f_{a_i}\)

考慮對\(a_l到a_r\)進行加\(x\)的操作,其實也就相當於他們合起來之後再進行操作,因為\(f_i=f_{i-1}+f_{i-2}\)

我們將所有的\(f_{i-1}\)看做一個整體,\(f_{i-2}\)看做一個整體,

之後就是快速冪的事情了

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int mod=1e9+7;
namespace IO
{
	void read(int &x)
	{
		x=0;
		int f=1;
		char c=getchar();
		while('0'>c||c>'9')
		{
			if(c=='-')	
				f=-1;
			c=getchar();
		}
		while('0'<=c&&c<='9')
		{
			x=(x<<3)+(x<<1)+c-'0';
			c=getchar();
		}
		x*=f;
	}
	void read(long long &x)
	{
		x=0;
		int f=1;
		char c=getchar();
		while('0'>c||c>'9')
		{
			if(c=='-')	
				f=-1;
			c=getchar();
		}
		while('0'<=c&&c<='9')
		{
			x=(x<<3)+(x<<1)+c-'0';
			c=getchar();
		}
		x*=f;
	}
	void write(int x)
	{
		if(x<10)
			putchar(x+'0');
		else
		{
			write(x/10);
			putchar(x%10+'0');
		}
	}
	void write(long long x)
	{
		if(x<10)
			putchar(x+'0');
		else
		{
			write(x/10);
			putchar(x%10+'0');
		}
	}
}
namespace lst
{
	struct mat
	{
		int n,m;
		long long a[3][3];
		mat()
		{
			n=m=0;
			memset(a,0,sizeof(a));
		}
		friend mat operator + (const mat &a,const mat &b)
		{
			mat c;
			c.n=a.n;
			c.m=a.m;
			for(int i=1;i<=a.n;i++)
				for(int j=1;j<=a.m;j++)
					c.a[i][j]=(a.a[i][j]+b.a[i][j])%mod;
			return c;
		}
		friend mat operator * (const mat &a,const mat &b)
		{
			mat c;
			c.n=a.n;
			c.m=b.m;
			for(int i=1;i<=a.n;i++)
				for(int k=1;k<=a.m;k++)
					for(int j=1;j<=b.m;j++)
						c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
			return c;
		}
		void pr()
		{
			for(int i=1;i<=n;i++)
			{
				for(int j=1;j<=m;j++)
					cout<<a[i][j]<<' ';
				cout<<endl;
			}
		}
	}bas,now,acc;
	struct tree
	{
		int l,r;
		mat f;//f_{x-1},f_{x}
		mat lazy;
	}tre[400005];
	mat qkpow(mat a,long long b)
	{
		if(b==0)
			return bas;
		if(b==1)
			return a;
		mat t=qkpow(a,b/2);
		t=t*t;
		if(b%2==1)
			t=t*a;
		return t;
	}
	void push_down(int k)
	{
		tre[k<<1].f=tre[k].lazy*tre[k<<1].f;
		tre[k<<1].lazy=tre[k].lazy*tre[k<<1].lazy;
		tre[k<<1|1].f=tre[k].lazy*tre[k<<1|1].f;
		tre[k<<1|1].lazy=tre[k].lazy*tre[k<<1|1].lazy;
		tre[k].lazy=bas;
	}
	void build(int l,int r,int k)
	{
		tre[k].l=l;
		tre[k].r=r;
		tre[k].lazy=bas;
		if(l==r)
		{
			int x;
			cin>>x;
			tre[k].f.n=2;
			tre[k].f.m=1;
			tre[k].f.a[1][1]=1;
			tre[k].f.a[2][1]=0;
			tre[k].f=qkpow(acc,x)*tre[k].f;
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,k<<1);
		build(mid+1,r,k<<1|1);
		tre[k].f=tre[k<<1].f+tre[k<<1|1].f;
	}
	void add(int l,int r,int k=1)
	{
		if(l>tre[k].r||tre[k].l>r)
			return;
		if(l<=tre[k].l&&tre[k].r<=r)
		{
			tre[k].f=now*tre[k].f;
			tre[k].lazy=now*tre[k].lazy;
			return;
		}
		push_down(k);
		add(l,r,k<<1);
		add(l,r,k<<1|1);
		tre[k].f=tre[k<<1].f+tre[k<<1|1].f;
	}
	long long ask(int l,int r,int k=1)
	{
		if(tre[k].l>r||l>tre[k].r)
			return 0;
		if(l<=tre[k].l&&tre[k].r<=r)
			return tre[k].f.a[2][1];
		push_down(k);
		return (ask(l,r,k<<1)+ask(l,r,k<<1|1))%mod;
	}
}
using namespace IO;
using namespace lst;
int n,m;
int opt,l,r;
long long x;
void init()
{
	bas.n=bas.m=2;
	for(int i=1;i<=2;i++)
		bas.a[i][i]=1;
	acc.n=acc.m=2;
	acc.a[1][2]=acc.a[2][1]=acc.a[2][2]=1;
}
int main()
{
	init();
	cin>>n>>m;
	build(1,n,1);
	for(int i=1;i<=m;i++)
	{
		read(opt);
		read(l);
		read(r);
		if(opt==1)
		{
			read(x);
			now=qkpow(acc,x);
			add(l,r);
		}
		else
		{
			write(ask(l,r));
			putchar('\n');
		}
	}
	return 0;
}