×

第六篇: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;
}

posted @ 2026-10-05 17:25  shiori123  阅读(2)  评论(0)    收藏  举报