Harry And Math Teacher

一、题目

点此看题
题目描述
n n n层楼,每层有两个门(编号为 1 / 2 1/2 1/2), i i i层到 i + 1 i+1 i+1的门两两相通,有下列操作:

  • i i i层到 i + 1 i+1 i+1层的 x , y x,y x,y两个门的状态改变(联通变成不同,不同变成联通)
  • 询问 x x x y y y的方案数,可以从 x x x任意门入, y y y任意门出。

数据范围
多组数据, 1 ≤ n , m ≤ 50000 1\leq n,m\leq 50000 1n,m50000

二、解法

考虑 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]也就是到了 i i i层的 1 / 2 1/2 1/2号门的方案数, p d [ i ] [ j ] pd[i][j] pd[i][j]表示 i , j i,j i,j号门之间有无通路,有下列转移:
d p [ i ] [ 0 ] = p d [ 0 ] [ 0 ] × d p [ i − 1 ] [ 0 ] + p d [ 1 ] [ 0 ] × d p [ i − 1 ] [ 1 ] dp[i][0]=pd[0][0]\times dp[i-1][0]+pd[1][0]\times dp[i-1][1] dp[i][0]=pd[0][0]×dp[i1][0]+pd[1][0]×dp[i1][1] d p [ i ] [ 1 ] = p d [ 0 ] [ 1 ] + d p [ i − 1 ] [ 0 ] + p d [ 1 ] [ 1 ] × d p [ i − 1 ] [ 1 ] dp[i][1]=pd[0][1]+dp[i-1][0]+pd[1][1]\times dp[i-1][1] dp[i][1]=pd[0][1]+dp[i1][0]+pd[1][1]×dp[i1][1]上述转移很像矩阵乘法的递推,我们可以把 [ x , y − 1 ] [x,y-1] [x,y1]之间的矩阵乘起来,然后对系数求和就是方案数。

然后发现可以用线段树优化,第一个操作就是一个单点修改,第二个操作就是一个区间查询,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long
const LL MOD = 1e9+7;
const LL MAXN = 100005;
LL read()
{
	LL x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m;LL x,y;
struct Matrix
{
	LL a[3][3];
	Matrix() {memset(a,0,sizeof a);}
	Matrix operator * (const Matrix &B)
	{
		Matrix R;
		for(LL i=1;i<=2;i++)
			for(LL j=1;j<=2;j++)
				for(LL k=1;k<=2;k++)
					R.a[i][k]=(R.a[i][k]+a[i][j]*B.a[j][k])%MOD;
		return R;
	}
}I,tr[MAXN*4];
void build(LL i,LL l,LL r)
{
	if(l==r)
	{
		tr[i].a[1][1]=tr[i].a[1][2]=tr[i].a[2][1]=tr[i].a[2][2]=1;
		return ;
	}
	LL mid=(l+r)/2;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	tr[i]=tr[i<<1]*tr[i<<1|1];
}
void updata(LL i,LL l,LL r,LL id)
{
	if(l==r)
	{
		tr[i].a[x][y]^=1;
		return ;
	}
	LL mid=(l+r)/2;
	if(mid>=id)
		updata(i<<1,l,mid,id);
	else
		updata(i<<1|1,mid+1,r,id);
	tr[i]=tr[i<<1]*tr[i<<1|1];
}
Matrix query(LL i,LL l,LL r)
{
	if(x>r || l>y || l>r) return I;
	if(x<=l && r<=y) return tr[i];
	LL mid=(l+r)/2;
	Matrix res=query(i<<1,l,mid);
	return res*query(i<<1|1,mid+1,r);
}
signed main()
{
	//freopen("fuck.in","r",stdin);
	//freopen("mine.out","w",stdout);
	I.a[1][1]=I.a[2][2]=1;
	while(scanf("%d %d",&n,&m)!=EOF)
	{
		n--;
		build(1,1,n);
		while(m--)
		{
			LL op=read();
			if(op==1)
			{
				LL p=read();x=read();y=read();
				updata(1,1,n,p);
			}
			else
			{
				x=read();y=read();
				if(x>y) swap(x,y);
				y--;
				Matrix t=query(1,1,n);
				printf("%lld\n",(t.a[1][1]+t.a[1][2]+t.a[2][1]+t.a[2][2])%MOD);
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值