原题链接:http://codeforces.com/problemset/problem/852/B
大致题意:有 L 层的城市群,每层城市群由n个不相连的城市构成,上一层的第 I 个城市到下一层的第 J 个城市的代价是B[ J ],从最顶层的入口到第1层城市I的代价为 A[ I ],从最底层的城市K到出口的代价为C[ k ] ,问有多少种方法使得从入口到出口的代价和为M的倍数(答案对1e9+7取余数)。( L<=10^5 , N<=10^6 , M<=100)
DP方程显然可以定为 f[ i ] [ j ]表示到了第 i 层花销对M取余为J的方法数,然后可以发现每层之间的转移方程是不会变的,于是就可以用矩阵乘法+快速幂来快速进行DP转移。
小细节就是到了第N-1层时就不能继续用原来的转移方程了,因为最后一层到出口的代价不一样了,最后特殊处理一下就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
inline void read(int &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void read(long long &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void write(int x){
static const int maxlen=100;
static char s[maxlen];
if (x<0) { putchar('-'); x=-x;}
if(!x){ putchar('0'); return; }
int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
for(int i=len-1;i>=0;--i) putchar(s[i]);
}
const int MAXN = 1100000;
int tot,l,lim;
int v[ MAXN ];
long long cnt[ 200 ];
struct Matrix{
int n,m;
long long a[120][120];
Matrix (){
memset( a, 0 ,sizeof( a )) ;
n=0,m=0;
}
void out(){
puts("");
for (int i=0;i<n;i++)
{
for (int j=0;j<m;j++)
printf("%d ",a[i][j]);
puts("");
}
puts("");
}
void init(){
memset(cnt , 0 ,sizeof( cnt ));
for (int i = 1;i<=tot;i++)
{
int x;
read(x);
x=x%lim;
v[i]=x;
cnt[x]++;
}
n=lim; m=lim;
for (int i=0;i<lim;i++)
for (int j=0;j<lim;j++)
a[ i ][ (i+j)%lim ]=cnt[j];
}
};
const long long P=1000000007ll;
Matrix operator * ( Matrix A, Matrix B ){
Matrix C;
C.n=A.n; C.m=B.m;
for (int i=0;i<A.n;i++)
for (int j=0;j<A.m;j++)
for (int k=0;k<B.m;k++)
C.a[i][k]= ( C.a[i][k] + 1ll*A.a[i][j] * B.a[j][k]%P )%P;
return C;
}
Matrix st,mid,ORG;
Matrix get_pow( Matrix A ,int x){
if (x==0)
return ORG;
Matrix tmp=get_pow( A ,x/2);
if ( x%2==0 )
return tmp*tmp;
else
return tmp*tmp*A;
}
int main(){
read(tot); read(l); read(lim);
ORG.n=lim;ORG.m=lim;
for (int i = 0;i<lim;i++)
ORG.a[i][i]=1;
st.n=1; st.m=lim;
for (int i = 1;i<=tot;i++)
{
int x;
read(x);
x=x%lim;
st.a[0][x]++;
}
mid.init();
memset(cnt,0,sizeof(cnt));
for (int i = 1;i<=tot;i++)
{
int x;
read(x);
cnt[ ( x + v[i] ) % lim ] ++;
}
mid=get_pow(mid,l-2);
//mid.out();
st=st*mid;
int ans=0;
for (int i=0;i<lim;i++)
if ( cnt [ i ] )
ans= ( ans + 1ll*st.a[0][ ( lim-i)%lim ]*cnt[ i ]%P )%P;
printf("%d\n",ans);
return 0;
}