码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛

时间:2019-08-27 20:54:34      阅读:77      评论:0      收藏:0      [点我收藏+]

标签:+=   div   prim   zoj   define   memset   sdoi   ble   problem   

[Sdoi2017]序列计数

题目大意https://www.lydsy.com/JudgeOnline/problem.php?id=4818.


题解

首先列出来一个递推式子

$f[i][0]$表示$i$个任意数的答案。

$f[i][1]$表示$i$个合数的答案。

转移的时候发现可以用矩阵优化这个过程。

至于怎么把矩阵建出来,我们可以开个桶来解决这个问题。

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int mod = 20170408 ;

char *p1, *p2, buf[100000];

#define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )

int rd() {
	int x = 0;
	char c = nc();
	while (c < 48) {
		c = nc();
	}
	while (c > 47) {
		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
	}
	return x;
}

int p, n, m;

struct Matr {
	int a[110][110];
	Matr() {memset(a, 0, sizeof a);}
	friend Matr operator * (const Matr &a, const Matr &b) {
		Matr re;
		for (int i = 0; i < p; i ++ ) {
			for (int j = 0; j < p; j ++ ) {
				for (int k = 0; k < p; k ++ ) {
					(re.a[i][j] += (ll)a.a[i][k] * b.a[k][j] % mod) %= mod;
				}
			}
		}
		return re;
	}
	friend Matr operator ^ (Matr x, int y) {
		Matr re;
		for (int i = 0; i < p; i ++ ) {
			re.a[i][i] = 1;
		}
		while (y) {
			if (y & 1) {
				re = re * x;
			}
			y >>= 1;
			x = x * x;
		}
		return re;
	}
}M, A;

int bu[110];

bool vis[20000010];

int prime[20000010], cnt;

int main() {
	n = rd(), m = rd(), p = rd();	
	for (int i = 0; i < p; i ++ ) {
		bu[i] = m / p;
		if (i) {
			if (i <= m % p) {
				bu[i] ++ ;
			}
		}
	}
	for (int i = 0; i < p; i ++ ) {
		for (int j = 0; j < p; j ++ ) {
			M.a[i][j] = bu[(j - i + p) % p];
		}
	}
	for (int i = 0; i < p; i ++ ) {
		A.a[0][i] = bu[i];
	}
	A = A * (M ^ (n - 1));
	int ans = A.a[0][0];
	vis[1] = true;
	for (int i = 2; i <= m; i ++ ) {
		if (!vis[i]) {
			prime[ ++ cnt] = i;
		}
		for (int j = 1; j <= cnt && (ll)i * prime[j] <= m; j ++ ) {
			vis[i * prime[j]] = true;
			if (i % prime[j] == 0) {
				break;
			}
		}
	}
	for (int i = 1; i <= m; i ++ ) {
		if (!vis[i]) {
			bu[i % p] -- ;
		}
	}
	for (int i = 0; i < p; i ++ ) {
		A.a[0][i] = bu[i];
	}
	for (int i = 0; i < p; i ++ ) {
		for (int j = 0; j < p; j ++ ) {
			M.a[i][j] = bu[(j - i + p) % p];
		}
	}
	A = A * (M ^ (n - 1));
	printf("%d\n", (ans - A.a[0][0] + mod) % mod);
	return 0;
}

小结:就是这种求存在的问题,可以转化成全部-不存在。

[bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛

标签:+=   div   prim   zoj   define   memset   sdoi   ble   problem   

原文地址:https://www.cnblogs.com/ShuraK/p/11420733.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!