[SDOI2017]序列計數 (矩陣加速,小容斥)

題面

Alice想要得到一個長度爲n的序列,序列中的數都是不超過m的正整數,而且這n個數的和是p的倍數。

Alice還希望,這n個數中,至少有一個數是質數。

Alice想知道,有多少個序列滿足她的要求。

輸入格式

一行三個數,n,m,p。

對100\%的數據,1<= n <=1e9,1<= m <= 2e7,1<= p <= 100

輸出格式

一行一個數,滿足Alice的要求的序列數量,答案對20170408取模。

題解

我很少寫這樣的矩陣快速冪的題解

由於p的範圍很小,所以p^3*log是可以過的

我們先求不加質數限制的方案數,再減去所有數都爲合數的方案。

這道題,其實我們要知道[1,m]中的這些數在p的剩餘系中的分佈就可以計算,

因爲對於不加質數限制的方案,就算值域在[1 + p,m + p]它的答案也是正確的。

設f[i][j]表示對於i個數,“ 其總和 % p = j ”的方案數,

我們發現,對於f,餘數是要相加的,而方案數是要相乘的,

f[i][j]=\sum_{k=1}^{p}f[i-1][k]*f[1][(j-k+p)\mod p]

根據這個來推一下矩陣,i = 1的情況要直接算:

V=[1,\;0,\;0,\;...\;0]

P=\begin{bmatrix} f[1][0] ,&f[1][1],&f[1][2], & ... &f[1][p-1] \\ f[1][p-1] ,&f[1][0],&f[1][1], & ... &f[1][p-1] \\ f[1][p-2] ,&f[1][p-1],&f[1][0], & ... &f[1][p-1] \\ \vdots &\vdots &\vdots & \ddots &\vdots \\ f[1][1] ,&f[1][2],&f[1][3], & ... &f[1][0] \\ \end{bmatrix}

然後答案矩陣 S=V\times P^n,這裏的n就是題目中的n,

再來推一下所有數都是合數的方案

其實只要用歐拉篩篩一遍合數就行,然後:

f'[i][j]表示對於i個合數,“ 其總和 % p = j ”的方案數,

我們發現,對於f,餘數是要相加的,而方案數是要相乘的,

f'[i][j]=\sum_{k=1}^{p}f'[i-1][k]*f'[1][(j-k+p)\mod p]

根據這個來推一下矩陣,i = 1的情況要直接算:

V'=[1,\;0,\;0,\;...\;0]

P'=\begin{bmatrix} f'[1][0] ,&f'[1][1],&f'[1][2], & ... &f'[1][p-1] \\ f'[1][p-1] ,&f'[1][0],&f'[1][1], & ... &f'[1][p-1] \\ f'[1][p-2] ,&f'[1][p-1],&f'[1][0], & ... &f'[1][p-1] \\ \vdots &\vdots &\vdots & \ddots &\vdots \\ f'[1][1] ,&f'[1][2],&f'[1][3], & ... &f'[1][0] \\ \end{bmatrix}

然後答案矩陣 S'=V'\times P'\;^n,這裏的n就是題目中的n,

最終的答案就是S[0][0]-S'[0][0]

別忘了要全程取模

CODE

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
#define LL long long
#define MAXN 200005
#pragma GCC optimize(2)
#pragma G++ optimize(3)
#define rg register
#define DB double
using namespace std;
inline int read() {
	int f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
	return x * f;
}
int mod = 20170408; 
int n,m,q,i,j,s,o,k,t;
struct matrix{
	int n,m;
	int a[105][105];
	matrix(){memset(a,0,sizeof(a));n = m = 1;}
}m1;
matrix operator * (matrix A,matrix B) {
	matrix C;
	C.n = A.n;C.m = B.m;
	for(rg int i = 1;i <= C.n;i ++) {
		for(rg int k = 1;k <= A.m;k ++) {
			for(rg int j = 1;j <= C.m;j ++) {
				C.a[i][j] = (C.a[i][j] + A.a[i][k] *1ll* B.a[k][j] % mod) % mod;
			}
		}
	}
	return C;
}
matrix qkpow(matrix a,LL b) {
	if(b == 0) return m1;
	if(b == 1) return a;
	matrix as = qkpow(a,b>>1);
	return as*as*qkpow(a,b&1);
}
int pri[5000005],cn;
bool f[20000005];
void sieve(int n) {
	f[1] = 1;cn = 0;
	for(rg int i = 2;i <= n;i ++) {
		if(!f[i]) pri[++cn] = i;
		for(rg int j = 1;j <= cn && i * pri[j] <= n;j ++) {
			f[i * pri[j]] = 1;
			if(i % pri[j] == 0) break;
		}
	}
}
int cnt[105],cnt2[105];
int main() {
	n = read();m = read();k = read();
	for(rg int i = 1;i <= k;i ++) m1.a[i][i] = 1;
	m1.n = m1.m = k;
	sieve(m);
	for(rg int i = 1;i <= m;i ++) {
		if(f[i]) cnt2[i % k] ++;
		cnt[i % k] ++;
	}
	matrix A,B,C,D;
	A.n = 1,A.m = B.n = B.m = k;
	for(rg int i = 1;i <= k;i ++) A.a[1][i] = cnt[i-1];
	for(rg int i = 1;i <= k;i ++) {
		for(rg int j = 1;j <= k;j ++) {
			B.a[j][i] = cnt[(k + i - j) % k];
		}
	}
	C = A * qkpow(B,n-1);
	A.n = 1,A.m = B.n = B.m = k;
	for(rg int i = 1;i <= k;i ++) A.a[1][i] = cnt2[i-1];
	for(rg int i = 1;i <= k;i ++) {
		for(rg int j = 1;j <= k;j ++) {
			B.a[j][i] = cnt2[(k + i - j) % k];
		}
	}
	D = A * qkpow(B,n-1);
	printf("%d\n",(C.a[1][1] + mod - D.a[1][1]) % mod);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章