第六篇:P1357
题解补完计划:第六篇
我们发现 \(m \le 5\),考虑状压 DP,我们定义 \(dp_{i,s}\) 表示当前到 \(i\),后五位的状态是 \(s\),其中 \(1\) 代表是 \(C\),\(0\) 是 \(P\)。
有转移:
\(dp_{i+1,s>>1|(op<<(m-1))} += dp_{i, s}[popcount(s>>1|(op<<(m-1))) <= k]\) 其中,\(op\) 表示 \(i+1\) 位置的状态,\([x]\) 是艾弗森括号,当 \(x\) 为真时它的值为 \(1\) 否则为 \(0\)。
我们发现这个 DP 是 \(O(n2^m)\) 而 \(n \le 10^{15}\), 显然过不了,因为 \(i\) 只会转移到 \(i+1\),考虑矩阵优化。
\(
M_{i,j} = \begin{cases}1, & \text{若 } \operatorname{popcount}(i) \le k \text{ 且 } j = \lfloor i/2 \rfloor, \\[4pt]
1, & \text{若 } \operatorname{popcount}(i) \le k \text{ 且 } j = \lfloor i/2 \rfloor \mid 2^{m-1} \text{ 且 } \operatorname{popcount}(j) \le k, \\[4pt]
0, & \text{其他}.
\end{cases}
\)
\(\text{其中} \quad (0 \le i,j < 2^m)\)
时间复杂度 \(O(2^m \log n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
#define intl long long
#define mod 1000000007
#define countll(a) __builtin_popcountll((a))
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl n,k,m,len,res;
struct Mat{
intl a[40][40];
Mat() {
memset(a,0,sizeof a);
}
Mat operator * (const Mat&b) const {
Mat c;memset(c.a,0,sizeof c.a);
For(k,0,len) For(i,0,len) For(j,0,len) {
(c.a[i][j] += a[i][k]*b.a[k][j]%mod) %= mod;
}
return c;
}
}mat,b;
Mat Fpow(Mat a,intl b) {
Mat ans;
For(i,0,len) ans.a[i][i] = 1;
for(;b;b>>=1,a=a*a) {
if(b&1) ans = ans*a;
}
return ans;
}
int main() {
n=read(),m=read(),k=read();
len = (1<<m)-1;
For(i,0,len) {
if(countll(i) > k) continue;
mat.a[i][i>>1] = 1;
if(countll((i>>1)|(1<<(m-1))) <= k)
mat.a[i][(i>>1)|(1<<(m-1))] = 1;
}
Mat ans = Fpow(mat,n);
For(i,0,len) (res += ans.a[i][i]) %= mod;
printf("%lld\n",res);
return 0;
}

浙公网安备 33010602011771号