SDOI2017 序列計數

2022-09-20 19:06:13 字數 2984 閱讀 9823

alice 想要得到乙個長度為 \(n\) 的正整數序列 \(\\),滿足:

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

由於題目有【\(n\) 個數】【和】【有否質數】這三個主要限制,因此:

設 \(f(i,j,1/0)\) 表示前 \(i\) 個數,和為 \(j\),是否有了乙個質數。

列出樸素 dp 方程:

\[f(i,j,0)=\sum_f(i-1,j-k,0)\times [k \text]\\

f(i,j,1)=\sum_f(i-1,j-k,1)+f(i-1,j-k,0)\times [k\text]

\]所求即為 \(\sum_f(n,i,1)\)。

發現具體的【和】不重要,因為只關心【和 \(\bmod p\)】,所以 \(j,k\) 均可在 \(\pmod p\) 意義下列舉。

設 \(a_k,b_k,c_k\) 分別表示 \(\mod p=k\) 的質數、非質數、數的個數。

\[f(i,j,0)=\sum_f(i-1,j-k,0)\times b_k\\

f(i,j,1)=\sum_f(i-1,j-k,1)\times c_k+f(i-1,j-k,0)\times a_k

\]所求即為 \(f(n,0,1)\)。

每次轉移在做一樣的事情,所以矩陣快速冪可以用來加速轉移。

dp 矩陣:

\[\left[

\begin

f_\ f_\ f_\ \cdots\ f_\ f_\ \cdots \ f_

\end

\right]\\

\]轉移矩陣:編號1

2p12

p1a0a1…b0

b1…2ap-1a0…

bp-1b0…

…ap-1……

bp-1……

…………

…a3……

………a2

a3………

…pa1a2…b1

b2…p+1c0c1…

0……cp-1c0…

0………

cp-1……

…………

…………

…………

…………

…0……

2pc1c2…

0……這樣就可以利用 \(o(p^3\log n)\) 時間得到,其中 \(\log n\) 為快速冪,\(p^3\) 為兩個 \((2p)^2\) 的矩陣的乘法。可以通過此題。

#include using namespace std;

const int n=205,m=2e7+5,mod=20170408;

int n,m,p,tot,a[n][n],f[n],prime[m],a[n],b[n],c[n];

bool v[m];

void mul(int f[n],int a[n][n])

void mul2(int a[n][n],int b[n][n])

void qp(int a[n][n],int b)

memcpy(a,c,sizeof(c));

}int main()

} for(int i=1;i<=m;i++)a[i%p]+=!v[i],b[i%p]+=v[i],c[i%p]++;

for(int i=1;i<=p;i++)for(int j=1;j<=p;j++)a[i][j]=b[(p-i+j)%p];

for(int i=1;i<=p;i++)for(int j=1;j<=p;j++)a[i][j+p]=a[(p-i+j)%p];

for(int i=1;i<=p;i++)for(int j=1;j<=p;j++)a[i+p][j+p]=c[(p-i+j)%p];

f[1]=1;

qp(a,n);

mul(f,a);

cout<\(p\) 再大一點就過不了了,但其實完全可以再優化。

觀察轉移矩陣,其實我們是知道的,第 \(a_...a_\) 其實就是 \(a_...a_\) 向下平移一格(環狀平移)得到的。其它三個矩形區域也是類似。即便在做了快速冪之後,這種格局也是不會改變的。考慮將這些很類似的列中只取乙個 \(p\times 1\) 的列作為代表進行計算這樣只用 \(o(p^2)\),並再用 \(o(p^2)\) 進行複製、平移。具體地,按原來矩乘方式計算結果矩陣中 \((1,1)...(p,1);(p+1,1)...(2p,1);(1,p+1)...(p,p+1);(p+1,p+1)...(2p,p+1)\) 這些元素的值,然後按照規則填充空白部分。複雜度 \(o(p^2\log n)\)。更加可以通過。

#include using namespace std;

const int n=205,m=2e7+5,mod=20170408;

int n,m,p,tot,a[n][n],f[n],prime[m],a[n],b[n],c[n];

bool v[m];

void mul(int f[n],int a[n][n])

void mul2(int a[n][n],int b[n][n])

for(int j=p+2;j<=2*p;j++)

memcpy(a,c,sizeof(c));

}void qp(int a[n][n],int b)

memcpy(a,c,sizeof(c));

}int main()

} for(int i=1;i<=m;i++)a[i%p]+=!v[i],b[i%p]+=v[i],c[i%p]++;

for(int i=1;i<=p;i++)for(int j=1;j<=p;j++)a[i][j]=b[(p-i+j)%p];

for(int i=1;i<=p;i++)for(int j=1;j<=p;j++)a[i][j+p]=a[(p-i+j)%p];

for(int i=1;i<=p;i++)for(int j=1;j<=p;j++)a[i+p][j+p]=c[(p-i+j)%p];

f[1]=1;

qp(a,n);

mul(f,a);

cout<}

Sdoi2017 序列計數

alice想要得到乙個長度為n的序列,序列中的數都是不超過m的正整數,而且這n個數的和是p的倍數。alice還希望 這n個數中,至少有乙個數是質數。alice想知道,有多少個序列滿足她的要求。一行三個數,n,m,p。1 n 10 9,1 m 2 10 7,1 p 100 一行乙個數,滿足alice的...

SDOI2017 序列計數

這道題非常的迷幻 首先我們要容斥 考慮記 dp i j 表示前 i 位 p j 的方案數 g i j 表示前 i 位只用合數 p j 的方案數 於是可以考慮最暴力的 dp 是 o nm p 的 但是並沒必要 我們可以提前處理 1 m 這些數 p 的值,用這些值來轉移就好了 也就是額外記乙個 cnt ...

Sdoi2017 序列計數

time limit 30 sec memory limit 128 mb submit 317 solved 210 alice想要得到乙個長度為n的序列,序列中的數都是不超過m的正整數,而且這n個數的和是p的倍數。alice還希望 這n個數中,至少有乙個數是質數。alice想知道,有多少個序列滿...