数论综合训练
- Magic Pairs
- problem
- solution
- code
- CF107D Crime Management
- problem
- solution
- code
- UVA12183 Top Secret
- problem
- solution
- code
- P3746 [六省联考2017]组合数问题
- problem
- solution
- code
Magic Pairs
problem
已知A0x+B0y≡0(modn)A_0x+B_0y\equiv 0\pmod nA0x+B0y≡0(modn)恒成立
求所有满足Ax+By≡0(modn)Ax+By\equiv 0\pmod nAx+By≡0(modn)成立的(A,B)(A,B)(A,B)数量,0≤A,B<N0\le A,B<N0≤A,B<N
solution
既然A0x+B0y≡0(modn)A_0x+B_0y\equiv 0\pmod nA0x+B0y≡0(modn)都已经成立了
那么2A0x+2B0y≡0(modn)2A_0x+2B_0y\equiv 0\pmod n2A0x+2B0y≡0(modn)也同样成立
换言之kA0x+kB0y≡0(modn)kA_0x+kB_0y\equiv 0\pmod nkA0x+kB0y≡0(modn)都成立
无脑倍加,还对A,BA,BA,B划了范围,肯定是会有周期循环的
注意A0,B0A_0,B_0A0,B0要先模一下nnn
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
设limi:ilim_i:ilimi:i字符所有限制之积
所有cnt相乘小于等于123,换言之:如果枚举每个字符iii的出现次数取模limilim_ilimi,哈希的总状态数不超过123123123
取模后的状态转移是固定的,现在iii出现了jjj次,那么下一个状态(j+1)%limi(j+1)\%lim_i(j+1)%limi就可以设为111(可以转移过去)
与长度无关,因此可以矩阵加速
但最后第一行的哪些答案是符合条件的,就还需要一次dfs
,确定对于每一个iii枚举的长度都必须至少是一个限制的倍数
到最后一层的时候,就去调用哈希字符串对应的编号,在矩阵中进行查询,累加到答案上
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
NNN个数排成一圈,一次操作为,每个位置的数+=L×+=L\times+=L×左+R×+R\times+R×右,保留xxx为整数
问SSS轮操作后每个位置的值
N<=1000,S<=230,x<=9N<=1000,S<=2^{30},x<=9N<=1000,S<=230,x<=9
solution
非常标准的矩乘转移加速题干
但是发现nnn级别在1e31e31e3,矩乘是n3n^3n3的复杂度,也就是说常规的矩乘不足以通过这道题
发现转移矩阵的每一行相当于上一行进行右移111位,这种特殊的矩阵称之为循环矩阵
只需要计算第一行的加速矩阵,然后每一行都可以根据一定的公示在第一行进行查找
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
设fi,j:f_{i,j}:fi,j: 前iii个物品选择物品数量取模kkk为jjj的方案数
fi,j=fi−1,j+fi−1,(j−1+k)%kf_{i,j}=f_{i-1,j}+f_{i-1,(j-1+k)\%k}fi,j=fi−1,j+fi−1,(j−1+k)%k
iii的转移只跟i−1i-1i−1有关,且jjj的转移也是固定的
可以矩乘加速
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;
}