数论四之综合训练——Magic Pairs,Crime Management,Top Secret,组合数问题

Magic Pairs

problem

已知 A 0 x + B 0 y ≡ 0 ( m o d n ) A_0x+B_0y\equiv 0\pmod n A0x+B0y0(modn)恒成立

求所有满足 A x + B y ≡ 0 ( m o d n ) Ax+By\equiv 0\pmod n Ax+By0(modn)成立的 ( A , B ) (A,B) (A,B)数量, 0 ≤ A , B < N 0\le A,B<N 0A,B<N

solution

既然 A 0 x + B 0 y ≡ 0 ( m o d n ) A_0x+B_0y\equiv 0\pmod n A0x+B0y0(modn)都已经成立了

那么 2 A 0 x + 2 B 0 y ≡ 0 ( m o d n ) 2A_0x+2B_0y\equiv 0\pmod n 2A0x+2B0y0(modn)​也同样成立

换言之 k A 0 x + k B 0 y ≡ 0 ( m o d n ) kA_0x+kB_0y\equiv 0\pmod n kA0x+kB0y0(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=fi1,j+fi1,(j1+k)%k

i i i的转移只跟 i − 1 i-1 i1有关,且 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值