随($rand$)
\(link\)
首先拿到这道题,先被他的这手分数虎住了。
然后回想起我做的\(minimax\)。
想起来一般出题人并不会故意难为你而搞成这种分数,一般都是为了简化计算的。
所以我也懒得推式子,直接写了一个转化函数,测试了一下加法乘法在他给出的意义下成不成立。
测完不出所料,很完美,所以不用担忧这个分数问题了。
inline int tr( int a , int b ){ return T( a , qpow( b , P , P + 2 ) ) ; }
// printf( "%ld %ld %ld\n" , tr( 1 , 5 ) , tr( 2 , 7 ) , tr( 17 , 35 ) ) ;
虽然很无脑,但这并不影响他是一个不错的方法。
然后才开始正式看题,起初并没有什么思路,看了一下数据范围,\(m<=1e9\),这是人都想带个\(log\)把它解决了。
然后我突发奇想手动模了一个样例,把\(m\)的过程搞出来,发现每次的系数都是一样的。
我就想了想矩阵,并觉得快速幂能解决。
当时没算明白复杂度,以为至少\(80pts\),所以花了很长时间调
调完之后跑\(50pts\)的数据,\(5s\)多,人直接傻了
不过最后脸比较白,常熟比较小,狗过\(50pts\),复杂度\(O(mod^3 * log(m))\)。
50pts
#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register int
#define printf Ruusupuu = printf
#define int long
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 1e3 + 10 ;
const int P = 1e9 + 5 ;
int n , m , M , Ruusupuu ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) ch |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline int qpow( int a , int b , int p ){
int ans = 1 ;
while( b ){
if( b & 1 ) ans = 1ll * ans * a % p ;
a = 1ll * a * a % p ; b >>= 1 ;
} return ans ;
}
inline int T( int a , int b ){ return 1ll * a * b % ( P + 2 ) ; }
inline int Tt( int a , int b ){ return 1ll * a * b % M ; }
inline int Jj( int a , int b ){ return ( a + b > M ) ? ( a + b - M ) : ( a + b ) ; }
inline int J( int a , int b ){ return ( a + b > P + 2 ) ? ( a + b - P - 2 ) : ( a + b ) ; }
inline int tr( int a , int b ){ return T( a , qpow( b , P , P + 2 ) ) ; }
int a [N] , x , b [N][N] ;
struct Mat{
int m [N][N] ;
int l , w ;
} A , B , an , ans ;
void sc(){
// printf( "%ld\n" , tr( 3 , 2 ) ) ;
n = read() , m = read() , M = read() ;
for( R i = 1 ; i <= n ; i ++ ) x = read() , a [x] ++ ;
for( R i = 1 ; i <= M ; i ++ ) A.m [1][i] = tr( a [i - 1] , n ) ; A.l = M , A.w = 1 ;
for( R i = 1 ; i <= M ; i ++ ){
for( R j = 1 ; j <= M ; j ++ ){
int t = Tt ( i - 1 , j - 1 ) ;
B.m [i][t + 1] = J ( B.m [i][t + 1] , A.m [1][j] ) ;
}
} B.w = B.l = M ;
}
Mat mul( Mat a , Mat b ){
Mat ans ;
// puts( "shit" ) ;
for( R i = 1 ; i <= a.l ; i ++ )
for( R j = 1 ; j <= a.l ; j ++ )
ans.m [i][j] = 0 ;
for( R i = 1 ; i <= a.w ; i ++ )
for( R j = 1 ; j <= b.l ; j ++ )
for( R k = 1 ; k <= a.l ; k ++ )
ans.m [i][j] = J ( ans.m [i][j] , T ( a.m [i][k] , b.m [k][j] ) ) ;
ans.l = a.l ;
ans.w = a.w ;
return ans ;
}
Mat qpow( Mat a , int b ){
Mat ans ; ans.l = ans.w = a.l ;
for( R i = 1 ; i <= a.l ; i ++ )
for( R j = 1 ; j <= a.l ; j ++ ){
if( i == j ) ans.m [i][j] = 1 ;
else ans.m [i][j] = 0 ;
}
while( b ){
if( b & 1 ) ans = mul( ans , a ) ;
a = mul( a , a ) ;
b >>= 1 ;
}
return ans ;
}
void work(){
an = mul( A , qpow( B , m - 1 ) ) ;
int sum = 0 ;
for( R i = 1 ; i <= M ; i ++ ) sum = J( sum , T ( an.m [1][i] , ( i - 1 ) ) ) ;
printf( "%ld\n" , sum ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
正解:原根转化循环矩阵优化矩阵乘法(折磨神的转化,先\(\%\)学长)
我们知道,\(O(mod^3 * log(m))\)是不行的,但是,有些东西可以确定。
- 每次转移的系数确实相同
- 正解\(m\)肯定需要带一个\(log\)
那唯一能优化的,只有矩阵乘法了。
我们在构建系数矩阵\(B\)的时候,是通过乘法构建的,这导致我们的系数矩阵平平无奇,只能\(mod^3\)暴力算
如果我们可以把乘法转化为加法,由于加法的特性\(a+b=(a-1)+(b+1)\),(乘法并不满足\(a*b=(a-1)*(b+1)\))导致我们构造出来的矩阵,下一行必定是上一行向右移一个单位的到的。
这样,在计算矩阵乘法的时候,我们只需要计算一行,就可以通过移动得到整个矩阵。
于是,矩阵乘法就变成\(n^2\),问题就可以解决了。
如何转化乘法为加法?
这个属实是因为我太菜了,没有看懂题目给的信息。
正确的回答就是原根。
他的作用是将\([1,\varphi(p)]\)内\(mod \ \ p\)的乘法,转化为\([0,\varphi(p)-1]\)内\(mod \ \ \varphi(p)\)的加法。
原理是欧拉定理\((x^a*x^b) \ mod \ p ==(x^{(a+b )\ mod \varphi(p)})mod p\)
貌似有很快的方法求原根,不过暴力貌似问题不大,因为一个数的所有原根对于我们的转化是等效的,所以求最小的就行了。
复杂度超不过\(( ^4\sqrt n)^2\).
之后就没什么难的了,建立一个映射数组,建系数的时候转过去,统计答案的时候转回来就行了
\(ps:\)我的循环矩阵乘法并不是主流写法,其实可以一行搞定,不过还要推一些东西,我们懒得推,所以有二倍的常数。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register int
#define printf Ruusupuu = printf
#define int long
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 1e3 + 10 ;
const int P = 1e9 + 5 ;
int n , m , M , Ruusupuu ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) ch |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline int qpow( int a , int b , int p ){
int ans = 1 ;
while( b ){
if( b & 1 ) ans = 1ll * ans * a % p ;
a = 1ll * a * a % p ; b >>= 1 ;
} return ans ;
}
inline int T( int a , int b ){ return 1ll * a * b % ( P + 2 ) ; }
inline int Tt( int a , int b ){ return 1ll * a * b % M ; }
inline int Jj( int a , int b ){ return ( a + b ) % ( M - 1 ) ; }
inline int J( int a , int b ){ return ( a + b > P + 2 ) ? ( a + b - P - 2 ) : ( a + b ) ; }
inline int tr( int a , int b ){ return T( a , qpow( b , P , P + 2 ) ) ; }
int a [N] , x , b [N][N] , rt , rl1 [N] , rl2 [N] ; // rl1 -> 原根i -> rt ^ i , rl2 -> rt^i -> i ;
bool fg [N] ;
struct Mat{
int m [N][N] ;
int l , w ;
} A , B , an , ans ;
void sc(){
// printf( "%ld\n" , tr( 3 , 2 ) ) ;
n = read() , m = read() , M = read() ;
for( R i = 1 ; i < M ; i ++ ){
memset( fg , 0 , sizeof( fg ) ) ;
for( R j = 1 ; j < M ; j ++ ){
int t = qpow( i , j , M ) ;
if( fg [t] ) break ;
else fg [t] = 1 ;
if( j == M - 1 && t == 1 ) rt = i ;
} if( rt ) break ;
} //printf( "%ld\n" , rt ) ;
for( R i = 0 ; i < M - 1 ; i ++ ){
int t = qpow( rt , i , M ) ;
rl1 [i] = t ; rl2 [t] = i ;
}
for( R i = 1 ; i <= n ; i ++ ) x = read() , a [rl2 [x]] ++ ;
for( R i = 0 ; i < M - 1 ; i ++ ) A.m [0][i] = tr( a [i] , n ) ; A.l = M - 1 , A.w = 1 ;
for( R i = 0 ; i < M - 1 ; i ++ ){
for( R j = 0 ; j < M - 1 ; j ++ ){
int t = Jj ( i , j ) ;
B.m [i][t] = J ( B.m [i][t] , A.m [0][j] ) ;
}
} B.w = B.l = M - 1 ;
}
Mat mul( Mat a , Mat b ){
Mat ans ;
// puts( "shit" ) ;
for( R i = 0 ; i < a.w ; i ++ )
for( R j = 0 ; j < a.l ; j ++ )
ans.m [i][j] = 0 ;
for( R i = 0 ; i < a.l ; i ++ )
for( R j = 0 ; j < b.w ; j ++ )
ans.m [0][i] = J( ans.m [0][i] , T ( a.m [0][j] , b.m [j][i] ) ) ;//, printf( "%ld %ld\n" , a.m [1][j] , b.m [j][i] ) ;
for( R i = 1 ; i < a.w ; i ++ ){
for( R j = 0 ; j < a.l ; j ++ )
if( j ) ans.m [i][j] = ans.m [i - 1][j - 1] ;
ans.m [i][0] = ans.m [i - 1][a.l - 1] ;
}
ans.l = a.l ;
ans.w = a.w ;
return ans ;
}
Mat qpow( Mat a , int b ){
Mat ans ; ans.l = ans.w = a.l ;
for( R i = 0 ; i < a.l ; i ++ )
for( R j = 0 ; j < a.l ; j ++ ){
if( i == j ) ans.m [i][j] = 1 ;
else ans.m [i][j] = 0 ;
}
while( b ){
if( b & 1 ) ans = mul( ans , a ) ;
a = mul( a , a ) ;
b >>= 1 ;
}
return ans ;
}
void work(){
an = qpow( B , m - 1 ) ;
an = mul( A , an ) ;
int sum = 0 ;
for( R i = 0 ; i < M - 1 ; i ++ ) sum = J( sum , T ( an.m [0][i] , rl1 [i] ) ) ;
printf( "%ld\n" , sum ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}

浙公网安备 33010602011771号