一、题目
点此看题
题目描述
有
层楼,每层有两个门(编号为
),
层到
的门两两相通,有下列操作:
- 把 层到 层的 两个门的状态改变(联通变成不同,不同变成联通)
- 询问 到 的方案数,可以从 任意门入, 任意门出。
数据范围
多组数据,
二、解法
考虑
也就是到了
层的
号门的方案数,
表示
号门之间有无通路,有下列转移:
上述转移很像矩阵乘法的递推,我们可以把
之间的矩阵乘起来,然后对系数求和就是方案数。
然后发现可以用线段树优化,第一个操作就是一个单点修改,第二个操作就是一个区间查询,时间复杂度 。
#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);
}
}
}
}