Word
【题目描述】
有一个星球要创造新的单词,单词有一些条件:
1. 字母集有p个元音和q个辅音,单词由字母构成
2. 每个单词最多有n个元音和n个辅音(同一元音或辅音可重复使用)
3. 每个单词中元音总是出现在所有辅音之前,可以没有元音或没有辅音
4. 每个单词至少有一个字母
5. 可以在字母上标记重音。元音中最多标记一个,辅音中也最多标记一
个,一个单词中最多标记两个字母为重音
6. 如果两个单词字母、字母顺序或者重音不同就认为这两个单词不同。
他们想要知道一共能创造多少不同的单词,由于答案可能很大,所以只要
输出答案mod m后的值。
【输入格式】
输入文件word.in 包含4 个正整数p,q, n, m
【输出格式】
输出文件word.out 包含一个非负整数表示能创造出的新单词数mod m
后的值。
【样例输入1】
1 1 1 9
【样例输出1】
8
【样例输入2】
2 3 2 1000
【样例输出2】
577
【样例输入3】
1 1 1000000000 1000000000
【样例输出3】
0
【数据规模】
对于30%的数据,p, q, n ≤ 7
对于60%的数据,n ≤ 100000
对于100%的数据,p,q, n, m ≤ 10^9
先讲讲60%
此题元音辅音并没什么联系,所以可以分开来算,最后统计结果时合并,减去长度都为0的情况即可,
对元音来说,长度为i时,
,每个位置可以p个数,共
,加重音还多i 中方案数
此复杂度显然为
100% 矩阵快速幂优化
第一次写矩阵,所以我只会暴力理解,
初始矩阵S是刚开始的状态,操作矩阵T是一种运算,经过一次矩乘将会到达下一种状态
看看如何构造:
将上面式子分解,得到
显然矩阵初始值为n+1
要乘上 p
还要加上n,必须加一维
发现n是递减的,所以再加一维,第三维使 n 递减,辅助第二维
所以最终矩阵为
之前说过矩乘可以理解为运算,并满足结合律,所以可以快速幂加速
第一次讲矩阵可能不太好,见谅
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
struct matrix {
int a[3][3];int x,y;
};
int p,q,n,P=1e9+7;
matrix operator * (matrix x,matrix y) {
matrix z;
memset(z.a,0,sizeof z.a);
for (int i=0;i<x.x;i++)
for (int j=0;j<y.y;j++) {
for (int k=0;k<y.y;k++)
z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%P)%P;
}
z.x=x.x;z.y=y.y;
return z;
}
int ksm(int x,int ln) {
matrix s,t;
memset(s.a,0,sizeof s.a);
s.a[0][0]=n+1;s.a[0][1]=n;s.a[0][2]=1;s.x=1;s.y=3;
memset(t.a,0,sizeof t.a);
t.a[0][0]=x;t.a[1][0]=1;t.a[1][1]=1;t.a[2][1]=-1;t.a[2][2]=1;t.x=3;t.y=3;
for (;ln;ln>>=1,t=t*t) if (ln&1) s=s*t;
return s.a[0][0];
}
signed main() {
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
scanf("%lld%lld%lld%lld",&p,&q,&n,&P);
printf("%lld",(ksm(p,n)*ksm(q,n)%P-1+P)%P);
}