链接
http://codevs.cn/problem/2873/
题解
首先,用M个点把这N天分开之后,各个段都是独立的,因此一定可以每一段分别算出然后乘起来得到总的方案数。
对于一个左端没有限制,右端有颜色限制的段,方案数明显是3^L,其中L是这一段的长度。一个右端没有限制,左端有限制的段是一样的。
因此先把最左和最右两端的答案统计下来。然后考虑中间那些段。
对于一个中间的段,其左右都有不能选择的颜色,单独拿出长度为L的一段,设f[i][j]表示考虑了前i个位置,最后一个位置涂第j种颜色的方案数。
易知f[i][1]=f[i-1][2]+f[i-1][3]+f[i-1][4],其余的同理,初始状态f[1][不能涂的颜色]=0,f[1][能涂的颜色]=1,最后把f[L][能涂的颜色]加起来就好了。到这里已经很显然了,矩阵快速幂。
以上就是这道题的一般思路。
对于这道题目,它有一个很好的性质,那就是它的转移矩阵只有对角线上是0,其余位置都是1。这样就产生了很好的性质。
上面展示了最左端不能填A的情况,容易发现,每一列的和总是3的幂,第i列的和是3^i。我们要求的就是最后一列的和减去不能涂的颜色的那个数。比如一共有6个格子,最后一个格子不能填B,那答案就是729-182。而182是可以算出来的,183=3^6-(3^6+3)/4。
怎样知道这个格子中是几呢?
不难发现,最开始不能涂的那个颜色的值总是和其它颜色不同,而且随着下标的奇偶的变化,这种颜色的方案数总是周期性地比其它格子大1或少1,而减去的那个数,既和下标的奇偶性有关,又和开始不能涂的颜色有关。
通过归纳总结,容易总结出规律:
当开头和结尾不能涂的颜色相同时,如果L是偶数,ans=3^n-(3^n + 3)/4;如果L是奇数,ans=3^n-(3^n + 3)/4
当开头和结尾不能涂的颜色不同时,如果L是奇数,ans=3^n-(3^n - 1)/4;如果L是奇数,ans=3^n-(3^n + 1)/4
这样就不需要矩阵了。
这里似乎可以出题呢。当矩阵除了对角线是0,其余位置是1时,上述规律总是满足。
代码
//矩阵快速幂
#include <cstdio>
#include <algorithm>
#define ll long long
#define mod 1000000007
using namespace std;
ll trans[5][5], one[5][5], N, M, ans;
struct str{ll pos, col;}s[20];
inline bool cmp(str A, str B){return A.pos<B.pos;}
void mat_cpy(ll a[][5], ll b[][5])
{
ll i, j;
a[0][0]=b[0][0], a[0][1]=b[0][1];
for(i=1;i<=a[0][0];i++)for(j=1;j<=a[0][1];j++)a[i][j]=b[i][j];
}
void mat_mult(ll a[][5], ll b[][5], ll c[][5])
{
ll i, j, k, t[5][5]={0};
t[0][0]=a[0][0];t[0][1]=b[0][1];
for(i=1;i<=t[0][0];i++)
for(j=1;j<=t[0][1];j++)
for(k=1;k<=a[0][1];k++)
t[i][j]=(t[i][j]+a[i][k]*b[k][j])%mod;
mat_cpy(c,t);
}
void mat_pow(ll a[][5], ll b, ll ans[][5])
{
ll t[5][5];
for(mat_cpy(ans,one),mat_cpy(t,a);b;b>>=1,mat_mult(t,t,t))
if(b&1)mat_mult(ans,t,ans);
}
ll pow(ll a, ll b)
{
ll ans, t;
for(ans=1,t=a;b;b>>=1,t=t*t%mod)
if(b&1)ans=ans*t%mod;
return ans;
}
void init()
{
ll i, j;
char c;
scanf("%lld%lld",&N,&M);
for(i=1;i<=M;i++)
{
scanf("%lld %c",&s[i].pos,&c);
s[i].col=c-'A'+1;
}
sort(s+1,s+M+1,cmp);
one[0][0]=one[0][1]=4;
for(i=1;i<=4;i++)one[i][i]=1;
trans[0][0]=trans[0][1]=4;
for(i=1;i<=4;i++)for(j=1;j<=4;j++)if(i!=j)trans[i][j]=1;
}
void work()
{
ll i, f[2][5], t[5][5], l;
f[0][0]=1,f[0][1]=4;
if(M==0){ans=4*pow(3,N-1)%mod;return;}
ans=pow(3,s[1].pos-1)*pow(3,N-s[M].pos)%mod;
for(i=1;i<M;i++)
{
l=s[i+1].pos-1-s[i].pos;
if(l==0)continue;
f[1][1]=f[1][2]=f[1][3]=f[1][4]=1;f[1][s[i].col]=0;
mat_pow(trans,l-1,t);
mat_mult(f,t,f);
ans=ans*(f[1][1]+f[1][2]+f[1][3]+f[1][4]-f[1][s[i+1].col])%mod;
}
}
int main()
{
init();
work();
printf("%lld",ans);
return 0;
}
//找规律
#include <cstdio>
#include <algorithm>
#define ll long long
#define mod 1000000007ll
using namespace std;
ll ans, N, M, inv4, num[20];
struct str
{
ll pos, col;
}s[20];
inline bool cmp(str A, str B)
{
return A.pos<B.pos;
}
ll pow(ll a, ll b)
{
ll ans, t;
for(ans=1,t=a;b;t=t*t%mod,b>>=1)if(b&1)ans=ans*t%mod;
return ans;
}
void input()
{
ll i;
char c;
scanf("%lld%lld",&N,&M);
for(i=1;i<=M;i++)
{
scanf("%lld %c",&s[i].pos,&c);
s[i].col=c-'A'+1;
}
sort(s+1,s+M+1,cmp);
}
void solve()
{
ll i, l, t;
if(M==0){ans=4*pow(3,N-1)%mod;return;}
ans=pow(3,s[1].pos-1)*pow(3,N-s[M].pos)%mod;
for(i=1;i<M;i++)
{
l=s[i+1].pos-1-s[i].pos;
if(s[i].col==s[i+1].col)
t= l&1 ? pow(3,l)-(pow(3,l)-3)*inv4 : pow(3,l)-(pow(3,l)+3)*inv4;
else
t= l&1 ? pow(3,l)-(pow(3,l)+1)*inv4 : pow(3,l)-(pow(3,l)-1)*inv4;
ans=t%mod*ans%mod;
}
}
inline void exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b){x=1;y=0;return;}
ll xx, yy;
exgcd(b,a%b,xx,yy);
x=yy, y=xx-a/b*yy;
}
int main()
{
ll y;
input();
exgcd(4,mod,inv4,y);
inv4=(inv4+mod)%mod;
solve();
printf("%lld",(ans+mod)%mod);
return 0;
}