数论综合训练
Magic Pairs
problem
已知 A 0 x + B 0 y ≡ 0 ( m o d n ) A_0x+B_0y\equiv 0\pmod n A0x+B0y≡0(modn)恒成立
求所有满足 A x + B y ≡ 0 ( m o d n ) Ax+By\equiv 0\pmod n Ax+By≡0(modn)成立的 ( A , B ) (A,B) (A,B)数量, 0 ≤ A , B < N 0\le A,B<N 0≤A,B<N
solution
既然 A 0 x + B 0 y ≡ 0 ( m o d n ) A_0x+B_0y\equiv 0\pmod n A0x+B0y≡0(modn)都已经成立了
那么 2 A 0 x + 2 B 0 y ≡ 0 ( m o d n ) 2A_0x+2B_0y\equiv 0\pmod n 2A0x+2B0y≡0(modn)也同样成立
换言之 k A 0 x + k B 0 y ≡ 0 ( m o d n ) kA_0x+kB_0y\equiv 0\pmod n kA0x+kB0y≡0(modn)都成立
无脑倍加,还对 A , B A,B A,B划了范围,肯定是会有周期循环的
注意 A 0 , B 0 A_0,B_0 A0,B0要先模一下 n n n
code
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
vector < pair < int, int > > ans;
int main() {
int n, a0, b0;
scanf( "%d %d %d", &n, &a0, &b0 );
a0 %= n, b0 %= n;
int a = a0, b = b0;
do {
ans.push_back( make_pair( a, b ) );
a = ( a + a0 ) % n;
b = ( b + b0 ) % n;
}while( a != a0 || b != b0 );
sort( ans.begin(), ans.end() );
printf( "%d\n", ans.size() );
for( int i = 0;i < ans.size();i ++ )
printf( "%d %d\n", ans[i].first, ans[i].second );
return 0;
}
CF107D Crime Management
problem
solution
设 l i m i : i lim_i:i limi:i字符所有限制之积
所有cnt相乘小于等于123,换言之:如果枚举每个字符 i i i的出现次数取模 l i m i lim_i limi,哈希的总状态数不超过 123 123 123
取模后的状态转移是固定的,现在 i i i出现了 j j j次,那么下一个状态 ( j + 1 ) % l i m i (j+1)\%lim_i (j+1)%limi就可以设为 1 1 1(可以转移过去)
与长度无关,因此可以矩阵加速
但最后第一行的哪些答案是符合条件的,就还需要一次dfs
,确定对于每一个
i
i
i枚举的长度都必须至少是一个限制的倍数
到最后一层的时候,就去调用哈希字符串对应的编号,在矩阵中进行查询,累加到答案上
code
#include <map>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define mod 12345
#define int long long
vector < int > G[27];
int n, m, cnt, ret;
int lim[27];
struct matrix {
int c[125][125];
matrix() { memset( c, 0, sizeof( c ) ); }
int * operator [] ( int i ) { return c[i]; }
matrix operator * ( matrix &t ) const {
matrix ans;
for( int i = 1;i <= 123;i ++ )
for( int j = 1;j <= 123;j ++ )
for( int k = 1;k <= 123;k ++ )
ans[i][j] = ( ans[i][j] + c[i][k] * t[k][j] ) % mod;
return ans;
}
}g;
matrix qkpow( matrix x, int y ) {
matrix ans;
for( int i = 1;i <= 123;i ++ )
ans[i][i] = 1;
while( y ) {
if( y & 1 ) ans = ans * x;
x = x * x;
y >>= 1;
}
return ans;
}
struct node {
int cnt[27];
node() { memset( cnt, 0, sizeof( cnt ) ); }
bool operator < ( const node &t ) const {
for( int i = 1;i <= 26;i ++ )
if( cnt[i] == t.cnt[i] ) continue;
else return cnt[i] < t.cnt[i];
return 0;
}
}h;
map < node, int > mp;
void dfs( int x, node s ) {
if( x > 26 ) {
mp[s] = ++ cnt;
return;
}
if( ! lim[x] ) dfs( x + 1, s );
for( int i = 0;i < lim[x];i ++ ) {
s.cnt[x] = i;
dfs( x + 1, s );
}
}
void calc( int x, node s ) {
if( x > 26 ) {
ret = ( ret + g[1][mp[s]] ) % mod;
return;
}
if( ! lim[x] ) calc( x + 1, s );
for( int i = 0;i < lim[x];i ++ ) {
for( auto j : G[x] )
if( i % j == 0 ) goto next;
continue;
next :
s.cnt[x] = i;
calc( x + 1, s );
}
}
signed main() {
scanf( "%lld %lld", &n, &m );
if( ! n ) return ! printf( "1\n" );
if( ! m ) return ! printf( "0\n" );
for( int i = 1;i <= m;i ++ ) {
char ch; int x, c;
scanf( "\n%c %lld", &ch, &x );
c = ch - 'A' + 1;
if( ! lim[c] ) lim[c] = x;
else lim[c] *= x;
G[c].push_back( x );
}
dfs( 1, h );
for( map < node, int > :: iterator it = mp.begin();it != mp.end();it ++ ) {
int id = it -> second; node now = it -> first;
for( int i = 1;i <= 26;i ++ ) {
if( ! lim[i] ) continue;
node t = now;
t.cnt[i] = ( now.cnt[i] + 1 ) % lim[i];
g[id][mp[t]] ++;
}
}
g = qkpow( g, n );
calc( 1, h );
printf( "%lld\n", ret );
return 0;
}
UVA12183 Top Secret
problem
N N N个数排成一圈,一次操作为,每个位置的数 + = L × +=L\times +=L×左 + R × +R\times +R×右,保留 x x x为整数
问 S S S轮操作后每个位置的值
N < = 1000 , S < = 2 30 , x < = 9 N<=1000,S<=2^{30},x<=9 N<=1000,S<=230,x<=9
solution
非常标准的矩乘转移加速题干
但是发现 n n n级别在 1 e 3 1e3 1e3,矩乘是 n 3 n^3 n3的复杂度,也就是说常规的矩乘不足以通过这道题
发现转移矩阵的每一行相当于上一行进行右移 1 1 1位,这种特殊的矩阵称之为循环矩阵
只需要计算第一行的加速矩阵,然后每一行都可以根据一定的公示在第一行进行查找
code
#include <cstdio>
#include <cstring>
#define maxn 1005
#define int long long
int n, s, l, r, x, mod, T;
int ret[maxn], h[maxn];
struct matrix {
int n;
int c[2][maxn];
void clear() { memset( c, 0, sizeof( c ) ); }
matrix() { clear(); }
int * operator [] ( int i ) { return c[i]; }
matrix operator * ( matrix &t ) const {
matrix ans; ans.n = n;
for( int k = 1;k <= n;k ++ )
for( int j = 1;j <= n;j ++ ) {
int i = ( j - k + 1 + n ) % n;
if( ! i ) i = n;
ans[1][j] = ( ans[1][j] + c[1][k] * t[1][i] ) % mod;
}
return ans;
}
}g;
matrix qkpow( matrix x, int y ) {
matrix ans; ans.n = x.n; ans[1][1] = 1;
while( y ) {
if( y & 1 ) ans = ans * x;
x = x * x;
y >>= 1;
}
return ans;
}
signed main() {
scanf( "%lld", &T );
while( T -- ) {
scanf( "%lld %lld %lld %lld %lld", &n, &s, &l, &r, &x );
mod = 1;
for( int i = 1;i <= x;i ++ )
mod = ( mod << 3 ) + ( mod << 1 );
g.clear(); g.n = n;
for( int i = 1;i <= n;i ++ )
scanf( "%lld", &h[i] ), ret[i] = 0;
g[1][1] = 1, g[1][n] = l, g[1][2] = r;
g = qkpow( g, s );
for( int k = 1;k <= n;k ++ )
for( int i = 1;i <= n;i ++ ) {
int j = ( k - i + 1 + n ) % n;
if( ! j ) j = n;
ret[i] = ( ret[i] + g[1][j] * h[k] ) % mod;
}
for( int i = 1;i < n;i ++ )
printf( "%lld ", ret[i] );
printf( "%lld\n", ret[n] );
}
return 0;
}
P3746 [六省联考2017]组合数问题
problem
solution
设 f i , j : f_{i,j}: fi,j: 前 i i i个物品选择物品数量取模 k k k为 j j j的方案数
f i , j = f i − 1 , j + f i − 1 , ( j − 1 + k ) % k f_{i,j}=f_{i-1,j}+f_{i-1,(j-1+k)\%k} fi,j=fi−1,j+fi−1,(j−1+k)%k
i i i的转移只跟 i − 1 i-1 i−1有关,且 j j j的转移也是固定的
可以矩乘加速
code
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
#define maxn 50
int n, p, K, r;
struct matrix {
int c[maxn][maxn];
matrix() {
memset( c, 0, sizeof( c ) );
}
int * operator [] ( int i ) {
return c[i];
}
matrix operator * ( matrix &t ) const {
matrix ans;
for( int i = 0;i < K;i ++ )
for( int j = 0;j < K;j ++ )
for( int k = 0;k < K;k ++ )
ans[i][j] = ( ans[i][j] + c[i][k] * t[k][j] ) % p;
return ans;
}
}ret, g;
matrix qkpow( matrix x, int y ) {
matrix ans;
for( int i = 0;i < K;i ++ )
ans[i][i] = 1;
while( y ) {
if( y & 1 ) ans = ans * x;
x = x * x;
y >>= 1;
}
return ans;
}
signed main() {
scanf( "%lld %lld %lld %lld", &n, &p, &K, &r );
ret[0][0] = 1;
for( int i = 0;i < K;i ++ )
g[i][i] ++, g[i][( i - 1 + K ) % K] ++;
g = qkpow( g, n * K );
ret = ret * g;
printf( "%lld\n", ret[0][r] );
return 0;
}